区间价值 HihoCoder

来源:互联网 发布:吃东西知乎 编辑:程序博客网 时间:2024/05/16 12:48

给定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小的值是多少。

Input

第一行一个数T(T<=10),表示数据组数。

对于每一组数据:

第一行两个数n,k(1<=n<=200,000,1<=k<=n*(n+1)/2)

第二行n个数A1…An(1<=Ai<=1,000,000,000)

Output

一个数表示答案。

Sample Input
24 71 1 2 33 6100 100 100
Sample Output
03


思路:我们仔细考虑,可以发现一个价值与区间个数的单调性。区间越短,价值越小,这样的区间个数就越少。

然后统计小于等于当前二分的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;}




原创粉丝点击