一道二分题目,题目难在不容易看出二分,看出二分难度基本就没了。。。(我不会)
考虑可以被二分的原因:对于a数组中第k大的数比t大的子串的个数是随着t的减少而严格增大的,所以可以二分t,去寻找答案。
对于如何查询确定t时,符合条件的子串数量:定义一个右端点和一个左端点,枚举右端点,求每次左端点的最大值,让其包含至少k个大于t的数即可。
我的思路:我以为是要算每个数被计算的可能性,去求贡献,想了很久都做不出来,感觉二分的方法还是比较独特的,你不能根据每种情况的影响因素去分析如何优化,而是本身题目答案的单调性确保你可以使用二分计算出答案,而和这道题的题目性质本身没有什么关系。如果对二分不是很熟悉的人应该都看不出二分吧。。。因为正常人不会对这种题观察答案的单调性吧。。。
贴一下AC代码
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <string> #include <iostream> #include <queue> using namespace std; const int maxn = 1e5+10; typedef long long ll; ll a[maxn],k,n,m; ll J(int t) { ll ans=0,num=0; for(int i=0,j=-1;i<n;i++) { if(a[i]>=t) num++; while(i-j>k&&num>=k) { if(a[j+1]<t) { j++; } else { if(num==k) { break; } else { num--; j++; } } } if(num>=k) ans+=j+2; } t; return ans; } int main() { int T; cin>>T; while(T--) { cin>>n>>k>>m; for(int i=0;i<n;i++) cin>>a[i]; int l=0,r=1e9+7,mid=0; J(3); while(r-1>l) { mid=(l+r)/2; if(J(mid)>=m) { l=mid; } else r=mid; } if(J(r)>=m) { cout<<r<<endl; } else cout<<l<<endl; } }