给定n个数A1...An,小Ho想了解AL..AR中有多少对元素值相同。小Ho把这个数目定义为区间[L,R]的价值,用v[L,R]表示。
例如1 1 1 2 2这五个数所组成的区间的价值为4。
现在小Ho想知道在所有的的v[L,R](1 <= L <= R <= n)中,第k小的值是多少。
第一行一个数T(T<=10),表示数据组数。
对于每一组数据:
第一行两个数n,k(1<=n<=200,000,1<=k<=n*(n+1)/2)
第二行n个数A1…An(1<=Ai<=1,000,000,000)
一个数表示答案。
2 4 7 1 1 2 3 3 6 100 100 100
0 3
思路:我们仔细考虑,可以发现一个价值与区间个数的单调性。区间越短,价值越小,这样的区间个数就越少。
然后统计小于等于当前二分的mid值的区间个数。跟k比较。如果大于等于k high = mid - 1,否则low = mid + 1.
统计区间个数的时候。要用一个比较巧的方法就是用一个双指针。枚举左端点,找到右端点。当前区间[l,r]符合条件那么[l+1,r]...[r,r]都符合条件。
具体的看一下代码吧。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 2e5+7;
long long a[MAXN],temp[MAXN];
int vis[MAXN];
int n;
long long k;
bool check(long long mid)
{
int ed = 0;
long long sum = 0,num = 0;
memset(vis,0,sizeof vis);
for(int i = 0 ; i < n ; ++i)
{
while(ed < n && sum + vis[a[ed]] <= mid)
{
sum += vis[a[ed]];
vis[a[ed]]++;
ed++;
}
num += ed - i;
vis[a[i]]--;
sum -= vis[a[i]];
}
return num >= k;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%lld",&n,&k);
for(int i = 0 ; i < n ; ++i)
{
scanf("%lld",&a[i]);
temp[i] = a[i];
}
int cnt;
sort(temp,temp+n);
cnt = unique(temp,temp+n) - temp;
for(int i = 0 ; i < n ; ++i)a[i] = lower_bound(temp,temp+cnt,a[i]) - temp;
long long low = 0, high = n*(n-1LL)/2;
long long ans,mid;
while(low <= high)
{
mid = (low + high)>>1LL;
if(check(mid))
{
ans = mid;
high = mid -1;
}
else low = mid + 1;
}
printf("%lld\n",ans);
}
return 0;
}