[HAOI2017]新型城市化

题目大意

一副图告诉你那些点之间没有连边,并保证整幅图可以被划分为至多两个团。
请你找出每一对没有连边的点,使得它们连边后,图中最大团的大小会变大。

模型转化

因为原图要被分成至多两个团,其补图一定是二分图。
最大团大小等同于最大独立集大小,因此和最大匹配挂钩。
求哪些边一定在最大匹配上。

做法

先做最大匹配算法。
一定在最大匹配上的边一定是匹配边(显然)。
然后考虑三种情况会使得匹配边u->v不一定在最大匹配中。
1、在残余网络中,u可以到达T。
如果退流u->v,那么S就可以走u,又因为u能到达T,最大匹配不会变,则u->v不一定在最大匹配中。
2、在残余网络中,S可以到达v。
同理。
这两种情况随便搜一遍就可以判。
3、在残余网络中,u可以到达v。
如果退流u->v,那么S走到u,u走到v,v再到T,最大匹配不会变。
则u->v不一定在最大匹配中。
这种情况注意u->v是匹配边,所以v连了有向边到u,因此u和v处在一个环中。可以做一遍Tarjan,就能判掉。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=10000+10,maxm=150000+10;
struct dong{
    int x,y;
} ans[maxm];
int h2[maxn],g2[maxm*2],n2[maxm*2];
int h[maxn],go[maxm*2],next[maxm*2];
int h3[maxn],g3[maxm*2],n3[maxm*2];
int co[maxn],re[maxn],A[maxn],B[maxn],belong[maxn],dfn[maxn],low[maxn],sta[maxn];
int pd[maxn];
bool bz[maxn];
int i,j,k,l,r,s,t,n,m,tot,top,cnt,cnt1,cnt2;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
void add2(int x,int y){
    g2[++cnt]=y;
    n2[cnt]=h2[x];
    h2[x]=cnt;
}
void add3(int x,int y){
    g3[++cnt]=y;
    n3[cnt]=h3[x];
    h3[x]=cnt;
}
void dfs(int x,int y){
    co[x]=y;
    if (y) B[++cnt2]=x;else A[++cnt1]=x;
    int t=h2[x];
    bz[x]=1;
    while (t){
        if (!bz[g2[t]]) dfs(g2[t],1-y);
        t=n2[t];
    }
}
int hungary(int x){
    int t=h[x];
    while (t){
        if (pd[go[t]]<j){
            pd[go[t]]=j;
            if (re[go[t]]==0||hungary(re[go[t]])){
                re[go[t]]=x;
                return 1;
            }
        }
        t=next[t];
    }
    return 0;
}
void dg(int x){
    co[x]=1;
    bz[x]=1;
    int t=h[x];
    while (t){
        if (!bz[go[t]]&&re[go[t]]!=x){
            bz[go[t]]=1;
            co[go[t]]=1;
            if (re[go[t]]&&!bz[re[go[t]]]) dg(re[go[t]]);
        }
        t=next[t];
    }
}
void travel(int x,int y){
    co[x]=2;
    bz[x]=1;
    if (y==1){
        int t=h[x];
        while (t){
            if (re[x]!=go[t]&&!bz[go[t]]) travel(go[t],0);
            t=next[t];
        }
    }
    else{
        int t=h[x];
        while (t){
            if (re[go[t]]==x&&!bz[go[t]]) travel(go[t],1);
            t=next[t];
        }
    }
}
bool cmp(dong a,dong b){
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
void tarjan(int x){
    bz[x]=1;
    sta[++top]=x;
    pd[x]=1;
    dfn[x]=low[x]=++cnt;
    int t=h3[x];
    while (t){
        if (!bz[g3[t]]){
            tarjan(g3[t]);
            low[x]=min(low[x],low[g3[t]]);
        }
        else if (pd[g3[t]]) low[x]=min(low[x],dfn[g3[t]]);
        t=n3[t];
    }
    if (dfn[x]==low[x]){
        ++tot;
        do{
            belong[sta[top]]=tot;
            pd[sta[top]]=0;
            top--;
        }while (sta[top+1]!=x);
    }
}
int main(){
    freopen("a.in","r",stdin);freopen("a.out","w",stdout);
    n=read();m=read();
    fo(i,1,m){
        j=read();k=read();
        add2(j,k);add2(k,j);
    }
    fo(i,1,n) 
        if (!bz[i]) dfs(i,0);
    fo(i,1,m){
        j=g2[i*2-1];k=g2[i*2];
        if (co[j]) swap(j,k);
        add(j,k);add(k,j);
    }
    fo(i,1,cnt1){
        j++;
        hungary(A[i]);
    }
    fo(i,1,n) co[i]=bz[i]=pd[i]=0;
    fo(i,1,cnt2){
        k=B[i];
        if (re[k]) pd[re[k]]=1;
    }
    fo(i,1,cnt1){
        k=A[i];
        if (!bz[k]&&!pd[k]) dg(k);
    }
    fo(i,1,n) bz[i]=0;
    fo(i,1,cnt2){
        k=B[i];
        if (!bz[k]&&re[k]==0) travel(k,1);
    }
    cnt=0;
    fo(i,1,cnt1){
        k=A[i];
        t=h[k];
        while (t){
            if (re[go[t]]==k) add3(go[t],k);else add3(k,go[t]);
            t=next[t];
        }
    }
    cnt=top=tot=0;
    fo(i,1,n) bz[i]=pd[i]=0;
    fo(i,1,n)
        if (!bz[i]) tarjan(i);
    top=0;
    fo(i,1,m){
        j=go[i*2];k=go[i*2-1];
        if (re[k]==j&&co[j]!=2&&co[k]!=1&&belong[j]!=belong[k]){
            if (j>k) swap(j,k);
            ans[++top].x=j;
            ans[top].y=k;
        }
    }
    sort(ans+1,ans+top+1,cmp);
    printf("%d\n",top);
    fo(i,1,top) printf("%d %d\n",ans[i].x,ans[i].y);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值