Codeforces 1552 E. Colors and Intervals —— 贪心,sorting(双语)

本文探讨了一道关于给定数组元素分配区间的问题,要求每个值恰好出现在一个区间中,同时确保每个值最多被覆盖的区间数量符合限制。博主通过贪心策略和线段树数据结构,详细介绍了C++和Python代码实现,以及对解法的理论证明。
摘要由CSDN通过智能技术生成

This way

题意:

给你n*k个数a,每个数的值都在[1,n]之间,且每种值正好有k个,让你输出n个区间,
1.第i个区间 x i , y i x_i,y_i xi,yi要满足 a [ x i ] = a [ y i ] = i a[x_i]=a[y_i]=i a[xi]=a[yi]=i
2.且对于数组里面所有的数,它最多只能被包含在 ⌈ n k − 1 ⌉ ⌈\frac{n}{k−1}⌉ k1n个区间里。

题解:

其实平时做到这种题目就是连蒙带猜写的…感觉上答案对了且暂时找不到反例就写写试试。但是写博客还是得稍微地证明一下。
一般这种题目都是贪心地从前往后做,那我想到的就是先把所有满足条件1的区间按右端点从小到大排序。然后贪心地找值未出现过的右端点最小的区间,并且还要看这个区间是否满足第二个条件。那么我就用线段树来维护区间和和区间最大值,然后查找最右端的不能再被覆盖的点。
那么证明题解里面有写,就是如果按照这样子做某个值x是取不到的,那么任意两个x之间一定是被 ⌈ n k − 1 ⌉ ⌈\frac{n}{k−1}⌉ k1n个不同的值覆盖了,然后总共除了x会有k-1个不同的值。也就是总长度至少为(k-1)* ⌈ n k − 1 ⌉ ⌈\frac{n}{k−1}⌉ k1n>=n,那么是不可能存在的。

C++代码:

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int a[N*N],n,k,mx[N*N*4],f[N*N*4],val;
void push_down(int root){
    if(!f[root])return ;
    mx[root<<1]+=f[root];
    mx[root<<1|1]+=f[root];
    f[root<<1]+=f[root];
    f[root<<1|1]+=f[root];
    f[root]=0;
}
void update(int l,int r,int root,int ql,int qr){
    if(l>=ql&&r<=qr){
        mx[root]++;
        f[root]++;
        return ;
    }
    push_down(root);
    int mid=l+r>>1;
    if(mid>=ql)
        update(l,mid,root<<1,ql,qr);
    if(mid<qr)
        update(mid+1,r,root<<1|1,ql,qr);
    mx[root]=max(mx[root<<1],mx[root<<1|1]);
}
int query(int l,int r,int root){
    if(mx[root]<val)return -1;
    if(l==r)return l;
    int mid=l+r>>1;
    push_down(root);
    if(mx[root<<1|1]>=val)return query(mid+1,r,root<<1|1);
    return query(l,mid,root<<1);
}
int vis[N];
struct node{
    int l,r;
    bool operator< (const node& aa)const {
        return r<aa.r;
    }
}e[N*N];
int ans[N][2];
int main()
{
    scanf("%d%d",&n,&k);
    val=(n+k-1-1)/(k-1);
    int lef=1,res=n,all=0;
    for(int i=1;i<=n*k;i++){
        scanf("%d",&a[i]);
        if(vis[a[i]])e[++all]={vis[a[i]],i};
        vis[a[i]]=i;
    }
    sort(e+1,e+1+all);
    int rig=-1;
    for(int i=1;i<=all;i++){
        rig=query(1,n*k,1);
        if(e[i].r<=rig||e[i].l<=rig||ans[a[e[i].l]][0])continue;
        ans[a[e[i].l]][0]=e[i].l,ans[a[e[i].l]][1]=e[i].r;
        update(1,n*k,1,e[i].l,e[i].r);
    }
    for(int i=1;i<=n;i++)
        printf("%d %d\n",ans[i][0],ans[i][1]);
    return 0;
}

Python代码:

a=[0]*10005;mx=[0]*40005;f=[0]*40005
val=0
def push_down(root):
    if f[root]==0:return
    mx[root<<1]+=f[root]
    mx[root<<1|1]+=f[root]
    f[root<<1]+=f[root]
    f[root<<1|1]+=f[root]
    f[root]=0
def update(l,r,root,ql,qr):
    if l>=ql and r<=qr:
        mx[root]+=1
        f[root]+=1
        return
    push_down(root)
    mid=l+r>>1
    if mid>=ql:
        update(l,mid,root<<1,ql,qr)
    if mid<qr:
        update(mid+1,r,root<<1|1,ql,qr)
    mx[root]=max(mx[root<<1],mx[root<<1|1])
def query(l,r,root):
    if mx[root]<val:return -1
    if l==r:return l
    mid=l+r>>1
    push_down(root)
    if mx[root<<1|1]>=val :
        return query(mid+1,r,root<<1|1)
    return query(l,mid,root<<1)
vis=[0]*10005
ans=[[0]*2 for i in range(10005)]
n,k=map(int,input().split(' '))
val=(n+k-1-1)//(k-1)
lef=1;res=n
a=[0]
a+=list(map(int,input().split(' ')))
e=[]
for i in range(1,n*k+1):
    if vis[a[i]]!=0:
        e.append([vis[a[i]],i])
    vis[a[i]]=i
def cmp(x):
    return x[1]
e.sort(key=cmp)
rig=-1
all=len(e)
for i in range(all):
    rig=query(1,n*k,1)
    if e[i][1]<=rig or e[i][0]<=rig or ans[a[e[i][0]]][0]!=0:continue
    ans[a[e[i][0]]][0]=e[i][0];ans[a[e[i][0]]][1]=e[i][1]
    update(1,n*k,1,e[i][0],e[i][1])
for i in range(1,n+1):
    print(ans[i][0],ans[i][1])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值