[二分 前缀优化建图 2-SAT] Codeforces 587D. Duff in Mafia

二分答案

对于一个点,最多能删一条相连的边,每一种颜色最多能有一条相连的边,那么把每个点相连的边用前缀优化建图就可以了

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>

using namespace std;

const int N=1000010;

int n,m,cnt,tot,G[N];
struct iedge{
    int s,t,w,c,g;
}ie[N];
int tmp[N],icnt;
vector<iedge> e[N];
struct edge{
    int t,nx;
}E[N<<1];

inline void addedge(int x,int y,int c,int t,int g){
    e[x].push_back({x,y,t,c,g}); e[y].push_back({x,y,t,c,g});
}

vector<int> cc[N];
int tt[N],iit;

inline void addedge(int x,int y){
    //cout<<x<<' '<<y<<endl;
    E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
}

int vis[N],g[N],dfn[N],low[N],s[N],tp,iiit,ig;

void tarjan(int x){
    dfn[x]=low[x]=++iiit; vis[x]=1; s[++tp]=x;
    for(int i=G[x];i;i=E[i].nx){
        if(!vis[E[i].t]) tarjan(E[i].t);
        if(vis[E[i].t]!=2) low[x]=min(low[x],low[E[i].t]);
    }
    if(dfn[x]==low[x]){
        int k; ++ig;
        do{ k=s[tp--]; vis[k]=2; g[k]=ig; }while(tp && k!=x);
    }
}

int lst[N];

inline bool check(int x){
    memset(G,0,sizeof(G)); cnt=0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;i++)
        if(ie[i].w>x) addedge(i<<1|1,i<<1);
    tot=(m<<1|1)+1;
    for(int i=1;i<=n;i++){
        ++iit;
        vector<int> a,b;
        for(auto u : e[i]){
            if(tt[u.c]!=iit) tt[u.c]=iit,cc[u.c].clear(),a.push_back(u.c);
            cc[u.c].push_back(u.g); b.push_back(u.g);
        }
        if(!b.size()) continue;
        addedge(b[0]<<1|1,tot); addedge(tot+1,b[0]<<1);
        for(int j=1;j<b.size();j++){
            addedge(tot+(j-1<<1),tot+(j<<1));
            addedge(tot+(j<<1|1),tot+(j-1<<1|1));
            addedge(b[j]<<1|1,tot+(j<<1));
            addedge(tot+(j<<1|1),b[j]<<1);
            addedge(b[j]<<1|1,tot+(j-1<<1|1));
            addedge(tot+(j-1<<1),b[j]<<1);
        }
        tot+=(b.size()<<1|1)+1;
        for(int u : a){
            if(cc[u].size()<2) continue;
            addedge(cc[u][0]<<1,tot);
            addedge(tot+1,cc[u][0]<<1|1);
            for(int j=1;j<cc[u].size();j++){
                addedge(tot+(j-1<<1),tot+(j<<1));
                addedge(tot+(j<<1|1),tot+(j-1<<1|1));
                addedge(cc[u][j]<<1,tot+(j<<1));
                addedge(tot+(j<<1|1),cc[u][j]<<1|1);
                addedge(cc[u][j]<<1,tot+(j-1<<1|1));
                addedge(tot+(j-1<<1),cc[u][j]<<1|1);
            }
            tot+=(cc[u].size()<<1|1)+1;
        }
    }
    tp=iiit=ig=0;
    for(int i=2;i<=tot;i++)
        if(!vis[i]) tarjan(i);
    for(int i=1;i<=m;i++)
        if(g[i<<1]==g[i<<1|1]) return false;
    memcpy(lst,g,sizeof(g));
    return true;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,x,y,c,t;i<=m;i++)
        scanf("%d%d%d%d",&x,&y,&c,&t),ie[i]={x,y,t,c,i},tmp[++icnt]=c;
    sort(tmp+1,tmp+1+icnt); icnt=unique(tmp+1,tmp+1+icnt)-tmp-1;
    for(int i=1;i<=m;i++) ie[i].c=lower_bound(tmp+1,tmp+1+icnt,ie[i].c)-tmp;
    for(int i=1;i<=m;i++) addedge(ie[i].s,ie[i].t,ie[i].c,ie[i].t,i);
    int l=0,r=1e9,mid,ans=-1;
    //int ss=check(3);
    while(l<=r) 
        check(mid=l+r>>1)?r=(ans=mid)-1:l=mid+1;
    vector<int> pt;
    if(!~ans) return puts("No"),0;
    puts("Yes");
    for(int i=1;i<=m;i++)
        if(lst[i<<1|1]<lst[i<<1]) pt.push_back(i);
    printf("%d %d\n",ans,pt.size());
    for(int u : pt) printf("%d ",u);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值