区间价值 HihoCoder - 1483

给定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
2
4 7
1 1 2 3
3 6
100 100 100
Sample Output
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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值