码迷,mamicode.com
首页 > 其他好文 > 详细

HihoCoder - 1483 区间最值

时间:2018-07-31 10:59:43      阅读:201      评论:0      收藏:0      [点我收藏+]

标签:最大   turn   output   说明   type   can   组成   高级   check   

给定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\n",ans);
    }
}

 

HihoCoder - 1483 区间最值

标签:最大   turn   output   说明   type   can   组成   高级   check   

原文地址:https://www.cnblogs.com/Lis-/p/9393788.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!