本文介绍利用二分法求中位数的一般思路
如果一个题目要求中位数,二分答案,判定是否符合条件即可
题意
给一个n和一个k,n表示有一个数组有n个元素,求所有“区间长度大于等于k”的区间中,中位数最大的多少
做法
二分答案,检查是否存在一个大于等于k的区间满足即可,显然此时原数组元素具体值是多少已经不重要,重要的是元素的相对大小,设新数组b,若a[i]>=x,则b[i]=1,否则b[i]=0,设s为b的前缀和。
然后考虑对于每一个i,是否存在j (0<=j<=i-k),满足s[i]-s[j]>0,这是一个很常见的处理方式(称之为划分集合法),具体见代码
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <iostream> using namespace std; typedef long long ll; const ll mol=12345678; const int N=2e5+5; int a[N]; int b[N]; int s[N]; int n,k; bool check(int x){ for(int i=1;i<=n;i++){ if(a[i]>=x){ b[i]=1; } else b[i]=-1; s[i]=s[i-1]+b[i]; } int minn=0; for(int i=k;i<=n;i++){ minn=min(minn,s[i-k]); if(s[i]-minn>0) return 1; } return 0; }
int main() { cin>>n>>k; for(int i=1;i<=n;i++){ cin>>a[i]; } int l=1,r=n; int ans=0; while(l<=r){ int mid=l+r>>1; if(check(mid)){ ans=mid; l=mid+1; } else r=mid-1; } cout<<ans<<endl; return 0; }
|
icpc银牌题
题意
给一个长度为n的数组,问最多给一段区间添加等差数列后,序列第 k 大最大是多少。
等差数列首项为c, 公差为d,长度为m。
思路
这题虽然不是中位数,其实也差不多。都是二分答案检查是否符合即可
- 设二分答案x,若序列中某个数
a[i]<x,若使这个数>=x,则必须选一个位置作为开头,加等差数列。
- 对于所有数操作完后,我们
只可以选一个位置作为开头,那么要满足更多的a[i]>=x。很容易想到,对于每一个小于x的位置,这个开头是一段区间,我们差分维护即可,这样对所有数字操作完后,对差分数组求前缀和
- 最后,选出一个满足尽可能多的
a[i]的位置,如果此时可以满足的a[i]>=x还是大于等于k个,那么符合要求,否则不符合要求
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| #include<bits/stdc++.h> using namespace std; const int N=1e6+5; typedef long long ll; ll a[N]; ll n,m; ll k,c,d; ll cha[N]; bool check(ll x) { int num=0; for(int i=1; i<=n; i++) { if(a[i]<x) { if(d==0) { if(a[i]+c>=x) { cha[max(1ll,i-m+1)]++; cha[i+1]--; continue; } } else { ll ans=(c+a[i]-x+i*d)/d; ans=min(ans,(ll)i); if(ans>=1&&ans>=i-m+1) { cha[max(1ll,i-m+1)]++; cha[ans+1]--; } } } else num++; } ll maxn=0; for(int i=1; i<=n; i++) { cha[i]+=cha[i-1]; maxn=max(maxn,cha[i]); } for(int i=1; i<=n+1; i++)cha[i]=0; num+=maxn; return num>=k; } void solve() { cin>>n>>k>>m>>c>>d; for(int i=1; i<=n; i++) { cin>>a[i]; } ll ans=0; ll begin=1,end=2e15; while(begin<=end) { ll mid=begin+end>>1; if(check(mid)) { ans=mid; begin=mid+1; } else end=mid-1; } cout<<ans<<endl; } int main() { std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0); int t=1; while(t--) { solve(); } return 0; }
|
总结
碰到这种题先考虑二分吧,中位数,第k大