• HihoCoder


    给定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

    题意:我们给出n个数,我们求任意一段区间,他们相同的数的次数就是区间的值,然后我们按值排序求第k个区间的值是多少

    思路:开始我用的n2,果断超时。。然后我们看到ai的范围有这么大,我们又要记录次数,显然我们可以用map,但是我用map也超时了,
    所以有个高级的操作,因为n的范围数组能开的下,只是ai值大而已,所以我们可以离散化,然后我们想一下,怎么求答案呢,如果我们直接求出所有的区间再排序输出的话n2复杂度
    所以发现不行,我们仔细想想,我们能得知我们区间长度越小,我们的区间值肯定更小,我们可以二分去处理,二分的话最小值是0没有一个相同,最大的时候也就是全部的数都相同,可以推出是n*(n-1)/2
    因为我们要求是求第k个区间的值,那么我们就只要去寻找判断,小于当前数的区间个数有多少个,如果小于这个数的区间比k还大的话,说明我们当前的数肯定比我们要求的小,所以我们向右扩展,反之亦然

    然后我们想如何去求多少个区间比他小呢?
    我们可以不用求出所有区间的值为什么呢,因为我们区间的个数和值的大小息息相关
    如果[l.r]是比k小的,那么[l,r-1],[l,r-2]....[l,l]都是小于k的数,这里就用到了我们的尺取法
    那么我们就把它变成了一个nlogn的算法

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<map>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    ll a[200001];
    ll t,n,m,temp[200001];
    ll vis[200001];
    ll check(ll mid)//尺取求比mid小的区间个数
    {
        int i,j;
        ll sum=0;
        ll num=0;
        memset(vis,0,sizeof(vis));
        for(i=0,j=0;i<n;i++)
        {
            for(;j<n&&sum+vis[a[j]]<=mid;j++)
            {
                sum+=vis[a[j]];
                vis[a[j]]++;
            }
            num+=j-i;//尺取思想核心
            vis[a[i]]--;
            sum-=vis[a[i]];
        }
        return num>=m;
    }
    int main()
    {
        ll ans;
        scanf("%lld",&t);
        while(t--)
        {
            scanf("%lld%lld",&n,&m);
            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;
            ll left=0,right=((ll)n*((ll)n-1))/2;
            while(left<=right)
            {
                ll mid=(left+right)/2;
                if(check(mid))//如果小于mid的区间个数比m多的话,说明值还不够小
                {
                    ans=mid;
                    right=mid-1;
                }
                else{
                    left=mid+1;
                }
            }
            printf("%lld
    ",ans);
        }
    }
  • 相关阅读:
    iOS渠道分包2种模式之包内注入文件分包(iOS13验证签名问题)
    iOS13 新特性简介
    OC 字典dictionaryWithObjectsAndKeys报错
    博客迁移指南
    block内部实现原理(三)
    block内部实现原理(二)
    block内部实现原理(一)
    iOS:记一次Mac OS X 测试版(OS X EL Capitan) APP发布过程
    iOS: El Capitan Beta 下 Xcode6.4 不显示Scheme菜单
    iOS: UIWebView 中不加载图片(即浏览器常见的无图模式)
  • 原文地址:https://www.cnblogs.com/Lis-/p/9393788.html
Copyright © 2020-2023  润新知