gym102055 B. Balance of the Force(二分图染色+sort+线段树)

题意:

有n个人,每个人有两个权值L(i)和R(i),
每个人要么选择L要么选择R,
但是现在m对关系,每对关系的两个人不能选择同一方,
问每个人选择之后,所有人的权值最大值与最小值的差值最小是多少?
如果关系出现冲突,无法进行选择则输出Impossible。

数据范围:n,m<=2e5

解法:
二分图染色,如果染色冲突则无解,
否则每个连通块只有两种选择,记录每种选择的mi和ma,以及连通块的id
对所有选择按最大值ma从小到大排序,

设一共有num个连通块
枚举最大值ma,线段树T维护当前枚举左边的所有连通块的最小值.
当前面以及存在num个连通块时,则可以用ma-T.mi更新答案.
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=4e5+5;
vector<int>g[maxm];
int l[maxm],r[maxm];
struct Node{
    int mi,ma,id;
}e[maxm];
int mark[maxm];
int c[maxm];
int ok,tot;
int n,m;
struct Tree{
    int a[maxm<<2];
    inline void pp(int node){//pushup
        a[node]=min(a[node*2],a[node*2+1]);
    }
    void build(int l,int r,int node){//清空
        if(l==r){
            a[node]=1e9;
            return ;
        }
        int mid=(l+r)/2;
        build(l,mid,node*2);
        build(mid+1,r,node*2+1);
        pp(node);
    }
    void update(int x,int val,int l,int r,int node){//单点修改
        if(l==r){
            a[node]=val;
            return ;
        }
        int mid=(l+r)/2;
        if(x<=mid)update(x,val,l,mid,node*2);
        else update(x,val,mid+1,r,node*2+1);
        pp(node);
    }
    int ask(int x,int l,int r,int node){//单点查询
        if(l==r)return a[node];
        int mid=(l+r)/2;
        if(x<=mid)return ask(x,l,mid,node*2);
        else  return ask(x,mid+1,r,node*2+1);
    }
}T;
void dfs(int x,int col){//二分图染色
    if(!ok)return ;
    c[x]=col;
    if(col==1){
        e[tot].mi=min(e[tot].mi,l[x]);
        e[tot].ma=max(e[tot].ma,l[x]);
        e[tot-1].mi=min(e[tot-1].mi,r[x]);
        e[tot-1].ma=max(e[tot-1].ma,r[x]);
    }else{
        e[tot].mi=min(e[tot].mi,r[x]);
        e[tot].ma=max(e[tot].ma,r[x]);
        e[tot-1].mi=min(e[tot-1].mi,l[x]);
        e[tot-1].ma=max(e[tot-1].ma,l[x]);
    }
    for(int v:g[x]){
        if(c[v]==-1)dfs(v,!col);
        else if(c[v]==c[x])ok=0;
    }
}
bool cmp(Node a,Node b){
    return a.ma<b.ma;
}
signed main(){
    int TT;scanf("%d",&TT);
    int cas=1;
    while(TT--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)g[i].clear();
        for(int i=1;i<=m;i++){
            int a,b;scanf("%d%d",&a,&b);
            g[a].push_back(b);
            g[b].push_back(a);
        }
        for(int i=1;i<=n;i++){
            scanf("%d%d",&l[i],&r[i]);
        }
        //
        for(int i=1;i<=n;i++)c[i]=-1;
        ok=1,tot=0;
        int num=0;
        for(int i=1;i<=n&&ok;i++){
            if(c[i]==-1){
                num++;
                e[++tot]={(int)1e9,(int)-1e9,num};
                e[++tot]={(int)1e9,(int)-1e9,num};
                dfs(i,1);
            }
        }
        //
        printf("Case %d: ",cas++);
        if(!ok){
            puts("IMPOSSIBLE");
            continue;
        }
        //
        sort(e+1,e+1+tot,cmp);
        T.build(1,num,1);
        for(int i=1;i<=num;i++)mark[i]=0;
        int sum=0;
        int ans=1e9;
        for(int i=1;i<=tot;i++){
            if(e[i].ma<0)continue;
            if(!mark[e[i].id]){
                mark[e[i].id]=1;
                sum++;
            }
            int temp=T.ask(e[i].id,1,num,1);
            if(sum==num){
                T.update(e[i].id,e[i].mi,1,num,1);
                ans=min(ans,e[i].ma-T.a[1]);
            }
            if(temp==1e9)temp=e[i].mi;
            else temp=max(temp,e[i].mi);
            T.update(e[i].id,temp,1,num,1);
        }
        printf("%d\n",ans);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值