题解 CF1349B Orac and Medians

题目链接

我们可以把序列中的数,分为:小于(k)的,等于(k)的,和大于(k)的,三类。

首先,如果等于(k)的数数量为(0),则答案一定是( ext{no})。因为中位数只会在原有的数中产生,不会凭空冒出来一个没有出现过的值。

我们强行认为,我们的最后一次操作,是对整个序列求中位数(也就是操作区间为([l=1,r=n]))。之所以可以这样“强行认为”,是因为即使你已经用别的方法把序列变成了(a_1=a_2=dots=a_n=k),再来一次这样的最后操作也不会影响答案。

于是,钦定了最后一次操作是对整个序列求中位数后,我们的问题转化为:要使得序列里所有数,排好序后,中间位置的值刚好是(k)

考虑初始时的序列。如果(排好序后)中间位置已经是(k),则不用再操作了。否则,会有两种情况:

  • (>k)的数太多了。也就是说现在在中间位置上的,是一个(>k)的数。
  • (<k)的数太多了。也就是说现在在中间位置上的,是一个(<k)的数。

对于第一种情况。首先,序列里一定不全是(>k)的数(因为通过特判,我们保证了序列里至少有一个(=k)的数)。那么,就一定存在一对相邻的数,满足一个(>k),另一个(leq k)。我们每次对这样一对相邻的数操作,就能把那个(>k)的数换成(leq k)的数,也就是每次操作能使(>k)的数数量恰好减少(1)。那么,通过若干次操作,就一定能让整个序列(排好序后)中间位置上的值刚好是(k)。因此,第一种情况答案一定是( ext{yes})

对于第二种情况。我们要想办法通过一些操作将其转化为第一种情况。也就是说,要尽量把(<k)的数变成(geq k)的数。发现每次操作,需要区间里(geq k)的数的数量严格大于区间里(<k)的数的数量,我们才能把(<k)的数同化(变成(geq k)的数)。那么,用和上面一样的方法,每次合并相邻两个数,就不行了。考虑如果序列里存在连续的长度至少为(2)的一段数,满足他们全部(geq k),则可以从这一段数出发,不断把两侧的数同化(变成(geq k)的)。于是问题转化为如何使序列里至少有一段长度(geq 2)的全部(geq k)的数。

考虑求一个数组(f[i]),表示(1dots i)中,(geq k)的数的数量减去(<k)的数的数量的差。如果能找到一对((i,j)),满足(i-jgeq 2)(f[i]-f[j]>0)(也就是说区间([j+1,i])(geq k)的数量严格大于(<k)的数量),那么,可以通过一次操作使得([j+1,i])中所有数全部(geq k)。这样就得到了上一段所说的:长度(geq 2)的,所有数都(geq k)的区间。于是从这个区间出发,可以把更多的数变成(geq k)的数,这又转化为了一定有解的第一种情况。所以,在第二种情况下,只要找到了这样的((i,j)),则答案一定是( ext{yes}),否则答案一定是( ext{no})。找((i,j)),只需要枚举(i),维护(jin [1dots i-2])中所有(f[j])的最小值即可。

时间复杂度(O(n))

参考代码:

//problem:CF1349B
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

const int MAXN=1e5;
int n,K,a[MAXN+5],f[MAXN+5];

int main() {
	int T;cin>>T;while(T--){
		cin>>n>>K;
		int cntgreater=0,cntK=0;
		for(int i=1;i<=n;++i){
			cin>>a[i];
			if(a[i]==K)cntK++;
			else if(a[i]>K)cntgreater++;
		}
		if(!cntK){
			cout<<"no"<<endl;continue;
		}
		if(cntK+cntgreater>=n/2+1){
			cout<<"yes"<<endl;continue;
		}
		int mn=n;
		bool yes=false;
		for(int i=1;i<=n;++i){
			f[i]=f[i-1];
			if(a[i]>=K)f[i]++;
			else f[i]--;
			if(f[i]-mn>0){
				yes=true;
				break;
			}
			mn=min(mn,f[i-1]);
		}
		if(yes)cout<<"yes"<<endl;
		else cout<<"no"<<endl;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/dysyn1314/p/12881386.html