CF Median Splits (中位数映射+前缀和)
https://codeforces.com/contest/2103/problem/C
思路:要判断一个数组的中位数是否小于等于k,意味着这个数组需要至少ceil(m/2)个数是小于等于k的,我们不妨将小于等于k的数映射为1,大于k的数映射为-1,如果一个区间和满足大于等于0,那么其(小于等于k的个数)一定大于等于(大于k的个数),所以我们可以使用前缀和快速计算出子段和,这道题要我们划分三个区间,取每个区间的中位数,然后对这三个数取中位数要求结果<=k,所以至少要有两个区间的和是>=0的,三个区间的位置是左 中 右,我们可以先判断左 右两个区间是否满足,若不满足 接着判断 中 右,这里我们可以维护一个前缀和最小值,使“中”区间右端点i的前缀和-前缀和最小值 尽可能>=0,若此时中区间右端点对应i位置,那么右区间左端点对应i+1位置,其区间和为suf[i+1]对应的后缀和。最后一种情况是 判断 左 中两个区间是否满足 ,与前面提到的方法一样,这里不过多赘述。
Code:
void solve()
{int n,k;cin>>n>>k;vector<int> a(n+1),pre(n+1,0),suf(n+2,0);for(int i=1;i<=n;i++ ){cin>>a[i];a[i]=(a[i]>k?-1:1);pre[i]=pre[i-1]+a[i];}suf[n]=a[n];for(int i=n-1;i>=1;i--) suf[i]=suf[i+1]+a[i];int l=1,r=n;while(l<=n&&pre[l]<0) l++;while(r>=1&&suf[r]<0) r--;if(l<r){cout<<"YES"<<endl;return ;} int mind=pre[1];for(int i=2;i<=n;i++){if(pre[i]-mind>=0&&i+1<=n&&suf[i+1]>=0){cout<<"YES"<<endl;return ;}mind=min(mind,pre[i]);}mind=suf[n];for(int i=n-1;i>=1;i--){if(suf[i]-mind>=0&&i-1>=1&&pre[i-1]>=0){cout<<"YES"<<endl;return ;}mind=min(mind,suf[i]);}cout<<"NO"<<endl;}