Codeforces 1348 F. Phoenix and Memory —— 贪心,权值线段树,有丶东西

This way

题意:

现在有n个人,他们可行的取值范围是li~ri。让你构造一个排序使得它符合每个人的范围,问你这个排序是否是独一无二的。

题解:

首先找一个排序的话,只需要从小到大枚举值,然后查看当前有哪些人包含这个值,将右端点最小的那个人赋予这个值即可。因为这必然是最优的,取右端点最小的话,剩下的人的选择机会更多。
对于是否独一无二的问题,我们只需要选择两个人,使得max(lef[i],lef[j])<=v[i],v[j]<=min(rig[i],rig[j]),就像下面这个样子:
在这里插入图片描述
这样的话,他们的值可以互换。
那么我们从小枚举值,并且将包含这个值的人的值加入权值线段树(有点绕)
设val[i]表示第i个人的值是val[i]
p[i]表示值是i的人的序号为p[i]
那么上面的这个意思就是枚举的值是1~n,然后当前的人的序号是p[i],对于lef[x]<=i<=rig[x]的人的val[x]加入到权值线段树中的第val[x]位置。
接下来我们查询lef[i]~rig[i]中是否有人即可。
注意要将自己删掉。

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
const int N=2e5+5;
int lef[N],rig[N];
struct edge{
    int l,r,id;
    bool operator< (const edge& a)const {
        return l<a.l;
    }
}e[N],tmp[N];
bool cmp(edge x,edge y){return x.r<y.r;}
int p[N],val[N];
set<pa>s;
int num[N*4];
void update(int l,int r,int root,int p,int v){
    if(l==r){
        num[root]+=v;
        return ;
    }
    int mid=l+r>>1;
    if(mid>=p)
        update(l,mid,root<<1,p,v);
    else 
        update(mid+1,r,root<<1|1,p,v);
    num[root]=num[root<<1]+num[root<<1|1];
}
int query(int l,int r,int root,int ql,int qr){
    if(!num[root])return -1;
    if(l==r)return l;
    int mid=l+r>>1;
    if(l>=ql&&r<=qr){
        if(num[root<<1])
            return query(l,mid,root<<1,ql,qr);
        return query(mid+1,r,root<<1|1,ql,qr);
    }
    int ans=-1;
    if(mid>=ql)
        ans=query(l,mid,root<<1,ql,qr);
    if(mid<qr&&ans==-1)
        ans=query(mid+1,r,root<<1|1,ql,qr);
    return ans;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&lef[i],&rig[i]);
        e[i]=tmp[i]={lef[i],rig[i],i};
    }
        
    sort(e+1,e+1+n);
    int j=1;
    for(int i=1;i<=n;i++){
        while(j<=n&&i==e[j].l)s.insert({e[j].r,e[j].id}),j++;
        p[i]=(*s.begin()).second;
        s.erase(s.begin());
        val[p[i]]=i;
    }
    int f=0;
    sort(tmp+1,tmp+1+n,cmp);
    int now1=1,now2=1;
    for(int i=1;i<=n;i++){
        int id=p[i];
        while(now1<=n&&e[now1].l==i){
                update(1,n,1,val[e[now1].id],1);
            now1++;
        }
        update(1,n,1,val[id],-1);
        
        int ans=query(1,n,1,lef[id],rig[id]);
        if(~ans){
            printf("NO\n");
            for(int i=1;i<=n;i++)
                printf("%d%c",val[i]," \n"[i==n]);
            swap(val[id],val[p[ans]]);
            for(int i=1;i<=n;i++)
                printf("%d%c",val[i]," \n"[i==n]);
            return 0;
        }
    }
    printf("YES\n");
    for(int i=1;i<=n;i++)
        printf("%d%c",val[i]," \n"[i==n]);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值