NanoApe Loves Sequence Ⅱ
退役狗 NanoApe 滚回去学文化课啦! 在数学课上,NanoApe 心痒痒又玩起了数列。他在纸上随便写了一个长度为 n 的数列,他又根据心情写下了一个数 m。 他想知道这个数列中有多少个区间里的第 k 大的数不小于 m,当然首先这个区间必须至少要有 k 个数啦。
第一行为一个正整数 T,表示数据组数。 每组数据的第一行为三个整数 n,m,k。 第二行为 n 个整数 Ai,表示这个数列。 1≤T≤10, 2≤n≤200000, 1≤k≤n/2, 1≤m,Ai≤109
对于每组数据输出一行一个数表示答案。
1 7 4 2 4 2 7 7 6 5 1
18
思路:
1、首先我们暴力枚举一下样例:
其中:
4 2 7 7 6 5 1能够分成这么些个子序列:
起点为4:
4 2
4 2 7
4 2 7 7
4 2 7 7 6
4 2 7 7 6 5
4 2 7 7 6 5 1
起点为2:
2 7
2 7 7
2 7 7 6
2 7 7 6 5
2 7 7 6 5 1
起点为7:
7 7
7 7 6
7 7 6 5
7 7 6 5 1
起点为第二个7:
7 6
7 6 5
7 6 5 1
起点为6:
6 5
6 5 1
起点为5:
5 1
然后我们就能确定其可行方案了。在枚举的过程中,我们就能发现这样一个事实:
以任何一个数为起点开始向后枚举的过程中,如果枚举到了某一个数,使得以这个数为起点的子序列满足条件,那么之后枚举出来的子序列一定满足条件e.g:
4 2-----不可行
4 2 7-----可行
4 2 7 7-----可行
4 2 7 7 6-----可行
4 2 7 7 6 5-----可行
4 2 7 7 6 5 1-----可行
然后我们就能有这样一个暴力的思路:
枚举一个起点,从起点出发,如果枚举到了某一个数(第i个数),使得以这个数为起点的子序列满足了条件,那么output+=n-i+1;然后break;
那么其时间复杂度呢?最坏的情况我们可以抽象的看成O(N^2);显然会TLE,那么我们使用二分查找的方式优化。
2、
优化过程其实也并不难:维护一个数组sum【i】表示第i个数(包括这个数在内)有多少个数不小于m。显然这个数组的值是递增的、也就是说满足二分的条件。
那么我们一层for枚举起点,然后二分查找第一个出现的sum【i】,使得sum【i】-sum【起点-1】==k。
然后我们维护output即可。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll __int64
ll a[5000000];
ll sum[5000000];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ll n,m,k;
memset(a,0,sizeof(a));
memset(sum,0,sizeof(sum));
scanf("%I64d%I64d%I64d",&n,&m,&k);
for(int i=0;i<n;i++)
{
scanf("%I64d",&a[i]);
}
ll output=0;
for(int i=0;i<n;i++)
{
if(a[i]>=m)sum[i+1]=sum[i]+1;
else sum[i+1]=sum[i];
}
for(int i=1;i<=n-k+1;i++)
{
int l=i+k-1;
int r=n;
int mid;
int ans=-1;
while(r-l>=0)
{
mid=(l+r)/2;
if(sum[mid]-sum[i-1]>=k)
{
ans=mid;
r=mid-1;
}
if(sum[mid]-sum[i-1]<k)
{
l=mid+1;
}
}
if(ans==-1)continue;
output+=n-ans+1;
}
printf("%I64d\n",output);
}
}