Codeforces Round #768 (Div. 2) D题 Range and Partition

题面:

一句话题意:找一个闭区间 [x,y] ,把数组a分成k段子串,每段子串中在区间 [x,y] 范围内的ai的个数严格大于在区间 [x,y] 范围之外的数的个数。

我们可以知道,k个段内 xy区间内的数的个数 至少 比区间外数的个数大1,于是可以证明只要在数组a内 num(区间xy内的数) >= num(区间xy外的数)+k,则 无论数组怎样排列都可以有合理的划分。

接下来我们希望在至少nlogn时间复杂度内查找到一对差最小的xy使题意成立,这里我用的方法是维护两个指针xy,初始时使它们都等于0,每对xy统计在区间xy内数的个数是否满足条件:不满足条件则y++,增长区间;满足条件则x++,缩短区间找到xy差的最小值。

以下是代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a[N],cnt[N];
int xa,ya;
int main(){
    int t;cin>>t;
    int n,k,x,y,num,d,mind;
    while(t--){
        memset(cnt,0,sizeof cnt);
        cin>>n>>k;
        x=0;y=0;
        for(int i=0;i<n;++i){
            cin>>a[i];
            cnt[a[i]]++;
        }
        num=0;mind=N;d=0;//查找[x,y)最短区间,最终输出(x,y-1),查找方法类似滑窗
        while(y<=n){
            if(num-(n-num)<k){
                num+=cnt[y];
                y++;
            }else{
                if(d<mind){
                    mind=d;
                    xa=x;ya=y;
                }
                num-=cnt[x];x++;
            }
            d=y-x;
        }
        while(num-(n-num)>=k){
            if(d<mind){
                    mind=d;
                    xa=x;ya=y;
                }
                num-=cnt[x];x++;

            d=y-x;
        }
        ya=ya-1;
        cout<<xa<<" "<<ya<<endl;//第一个输出
        int i=0,j=0;num=0;//接下来划分数组a,
        int nnn=0;
        for(j;j<n;++j){
            if(a[j]<=ya&&a[j]>=xa){
                num++;
            }else{
                num--;
            }
            if(num==1){// 满足xy内的数个数比xy外数的个数多1就输出
                num=0;
                if(nnn==k-1){//最后一个划分结尾是n
                    cout<<i+1<<" "<<n<<endl;
                    break;
                }else{//前n-1个划分
                    cout<<i+1<<" "<<j+1<<endl;
                    nnn++;
                }
                i=j+1;
            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值