题解 CF1349B Orac and Medians
我们可以把序列中的数,分为:小于\(k\)的,等于\(k\)的,和大于\(k\)的,三类。
首先,如果等于\(k\)的数数量为\(0\),则答案一定是\(\text{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\)。因此,第一种情况答案一定是\(\text{yes}\)。
对于第二种情况。我们要想办法通过一些操作将其转化为第一种情况。也就是说,要尽量把\(<k\)的数变成\(\geq k\)的数。发现每次操作,需要区间里\(\geq k\)的数的数量严格大于区间里\(<k\)的数的数量,我们才能把\(<k\)的数同化(变成\(\geq k\)的数)。那么,用和上面一样的方法,每次合并相邻两个数,就不行了。考虑如果序列里存在连续的长度至少为\(2\)的一段数,满足他们全部\(\geq k\),则可以从这一段数出发,不断把两侧的数同化(变成\(\geq k\)的)。于是问题转化为如何使序列里至少有一段长度\(\geq 2\)的全部\(\geq k\)的数。
考虑求一个数组\(f[i]\),表示\(1\dots i\)中,\(\geq k\)的数的数量减去\(<k\)的数的数量的差。如果能找到一对\((i,j)\),满足\(i-j\geq 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)\),则答案一定是\(\text{yes}\),否则答案一定是\(\text{no}\)。找\((i,j)\),只需要枚举\(i\),维护\(j\in [1\dots 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;
}