解题报告:51nod 1686 第K大区间 二分+尺取

题目链接:

51nod 1686第k大区间

思路:

可以发现答案范围为1~n,那么考虑二分答案。

check函数可以用尺取的方式,每当r右移,左移l使得当前序列的值小于check的值,同时加上右边全部的序列个数(n-l+1)个,

这样就可以得到序列的值大于等于check的值的序列数。

需要注意的是输入的数组的值在Int范围内,可以直接用map,交了两发,一发T了,一发A了。。

考虑题目只关注数之间的相对大小关系而不关注数本身的值,于是可以将输入的数映射到对应的同样大小次序的数,这样最大的数就压缩到了数组的长度n,便可以直接用数组,不需要再用map,很稳。


代码:

#include<bits/stdc++.h>

using namespace std;

int n;
long long k;
int A[100005];
int B[100005];
int num[100005];

bool check(int x){
    memset(num,0,sizeof(num));
    long long res = 0;
    int mx = 0;
    int s = 1 , e = 1;
    for(e=1;e<=n;e++){
        mx = max(mx,++num[A[e]]);
        if(mx==x){
            while(mx==x){
                res += n-e+1;
                num[A[s]]--;
                if(num[A[s]]==mx-1){
                    mx--;
                }s++;
            }
        }
        if(res>=k){
            return true;
        }
    }return false;
}

bool cmp(int a,int b){
    return A[a]<A[b];
}

int main()
{
    while(scanf("%d%I64d",&n,&k)==2){
        for(int i=1;i<=n;i++){
            scanf("%d",&A[i]);
            B[i]=i;
        }sort(B+1,B+1+n,cmp);
        for(int i=1,j=1;i<=n;j++){
            int kk = i+1;
            while(kk<=n&&A[B[kk]]==A[B[i]])kk++;
            while(i<kk){
                A[B[i++]]=j;
            }
        }
        int s = 1 , e = n;
        while(s<e){
            int m = (s+e)>>1;
            if(m==s)break;
            if(check(m)){
                s = m;
            }else {
                e = m-1;
            }
        }if(check(e))s=e;
        printf("%d\n",s);
    }return 0;
}

/*
//直接用map

#include<bits/stdc++.h>

using namespace std;

int n;
long long k;
int A[100005];
map<int,int>num;
bool check(int x){
    num.clear();
    long long res = 0;
    int mx = 0;
    int s = 1 , e = 1;
    for(e=1;e<=n;e++){
        mx = max(mx,++num[A[e]]);
        if(mx==x){
            while(mx==x){
                res += n-e+1;
                if(--num[A[s++]]==mx-1){
                    mx--;
                }
            }
        }
        if(res>=k){
            return true;
        }
    }return false;
}

int main()
{
    while(scanf("%d%I64d",&n,&k)==2){
        for(int i=1;i<=n;i++){
            scanf("%d",&A[i]);
        }int s = 1 , e = n;
        while(s<e){
            int m = (s+e)>>1;
            if(m==s)break;
            if(check(m)){
                s = m;
            }else {
                e = m-1;
            }
        }if(check(e))s=e;
        printf("%d\n",s);
    }return 0;
}

*/


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值