题目大意:给一个整数序列,统计<k,m>子序列的数目。<k,m>序列是满足第k大的数字不比m小的连续子序列。
题目分析:维护一个不小于m的数的个数的后缀和数组,可以枚举序列起点,二分查找右端点序列最近的一个<k,m>序列。因为最近右端点是不减的,所以也可以用two-pointer在O(n)的时间复杂度内得到结果。
代码如下:
使用二分查找:
# include<iostream> # include<cstdio> # include<string> # include<cmath> # include<cstring> # include<algorithm> using namespace std; # define LL long long # define mid (l+(r-l)/2) const int N=200000; int a[N+5]; int sum[N+5]; int f(int l,int r,int val) { while(l<r){ if(sum[mid]>val) l=mid+1; else r=mid; } return l; } int main() { int T,n,m,k; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); for(int i=0;i<n;++i) scanf("%d",a+i); sum[n]=0; for(int i=n-1;i>=0;--i){ if(a[i]>=m) sum[i]=sum[i+1]+1; else sum[i]=sum[i+1]; } LL ans=0; for(int i=0;i+k-1<n;++i){ int id=f(i+k-1,n,sum[i]-k); if(sum[i]-sum[id]>=k) ans+=n-id+1; } cout<<ans<<endl; } return 0; }
使用two-pointer:
# include<cstdio> # include<iostream> # include<algorithm> using namespace std; # define LL long long const int N=200000; int a[N+5]; int main() { int T,n,m,k; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); for(int i=0;i<n;++i) scanf("%d",a+i); int p=0; int cnt=0; LL ans=0; for(int i=0;i+k-1<n;++i){ while(cnt<k&&p<n){ if(a[p]>=m) ++cnt; ++p; } if(cnt>=k) ans+=(LL)(n-p+1); if(a[i]>=m) --cnt; } printf("%lld ",ans); } return 0; }