codeforces1486D(二分答案,用+1-1判断是否是区间的中位数)

Problem - D - Codeforces

解题思路

要找到长度不小于k的子序列中最大的中位数,可以用二分答案的方法,可是要怎么个二分法呢,二分中位数吗,可是中位数怎么二分,设二分的数是x,难道是确定是否有子序列的中位数为x吗,如果是这样的话就不知道该怎么二分了,而且为了确定是不是中位数复杂度就爆了,因为很容易想到的暴力方法就是枚举每个区间然后排序,复杂度非常高,所以就可以用将大于等于x的数变成1,小于等于x的数变成-1,如果区间和是0 or 1,那么x就是这个区间的中位数,如果大于1就说明该区间比x比该区间的中位数小,小于0的话就是比区间的中位数大,这样就可以用二分了,要是x比所有区间的中位数都大的话就让x变小,反之变大,边界的时候就是答案了,那如何确定x比所有的区间中位数都大或者都小呢,可以用数组MIN保存所有的前缀里最多能删除的小于x的个数,如果区间1到n中大于等于x的数有s+10个,小于x的数有s+18个,所以他们的数量差是8个,而区间1到m(m<n)中比大于等于x的数和小于x的数的数量差最大时为9(小于x的数更多)分别时t和t+9,如果把这个区间的数都删掉,那么区间1到n中大于x的数的个数就是s+10-t,而小于x的数的个数就是s+18-t-9=s+9-t,他们的差值就变成了1,此时x就成了中位数,用这种方法就可以确定x是否比所有区间的中位数都大了。

代码

#include<bits/stdc++.h>
#define all(x) x.begin(),x.end()
#define int long long 
#define maxn 200005
#define endl "\n"
using namespace std;
int k;
vector<int>s(maxn),MIN(maxn);//不能够确定这个数是不是中位数
//但是能够确定这个数是在数列的左边还是右边,s大于0就是在左边,小于0就是在右边 
int check(vector<int>&a,int x){//这段代码的实际意义是人为制造单调性 
	int l=a.size()-1;
	for(int i=1;i<=l;i++){
		s[i]=s[i-1];
		if(a[i]>=x)s[i]++;
		else s[i]--;
		MIN[i]=MIN[i-1];
		MIN[i]=min(MIN[i],s[i]);
	}
	for(int i=k;i<=l;i++){
		if(s[i]-MIN[i-k]>=1)return 1;//MIN表示最多可以去掉的小于x的个数 
	}
	return 0;
}
void solve(){
	int n;
	cin>>n>>k;
	vector<int>a(n+1);
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int l=0,r=maxn;
	while(1){
		if(r-l<3){
			for(int i=r;i>=l;i--){
				if(check(a,i)==1){
					cout<<i<<endl;
					return;
				}
			}
		}
		int mid=(l+r)/2;
		if(check(a,mid)){
			l=mid;
		}
		else{
			r=mid; 
		}
	}
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int test=1;
    while(test--){
        solve();
    }
    return 0;
}


总结

对中位数的理解:
首先是为什么可以用大于x的数+1,小于的就-1来表示区间的中位数是否是x呢,因为对于中位数来说,大于他的数和小于他的数具体是什么没有意义,所以可以直接将数值进行转换,中位数的本质就是比中位数大的数的个数等于比中位数小的个数
对二分答案的理解:
不要拘泥于答案是不是符合条件,要寻找小于最终答案有什么共性,和大于最终答案有什么共性
对前缀和的理解:
前n个的和就是对前n个特性的累加,减去前缀m,就是将前n个的特性区间前m个的特性

posted @ 2025-04-27 20:58  C微  阅读(26)  评论(0)    收藏  举报