[HAOI2017]新型城市化 二分图+网络流+关键边

时间:2019-03-25
本文章向大家介绍[HAOI2017]新型城市化 二分图+网络流+关键边,主要包括[HAOI2017]新型城市化 二分图+网络流+关键边使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

HAOI2017

  • emm。一道模板题,本来想着30分钟写完,一点半赶紧开始打比赛……没想到,看题目理解题意花了20分钟,敲了10分钟。比赛完之后,写了30分钟吧,交上去果断0分。晚上调整思路之后,又写了一遍,仍然是0分。
  • 在神犇帮助下,找到了自己的错误:用网络流跑最大匹配,建的边都是有向边!

    Solution

  • 题目给定\(m\)对点对,这\(m\)对点对互相之间没有连边。其他的点互相之间都有边,问:把哪个点对连通之后,最大团会变大。
  • 在原图中是添加边使得最大团变大,对应在补图中当然就是割一条边,使得最大独立集变大。
  • 最大独立集=点数-最大匹配数。那么就是割一条边使得最大匹配数变小。也就是求二分图关键边。
  • 图中已经保证没有奇环,所以直接二分图染色。
  • 建边跑最大流。
  • tarjan求scc,判断关键边。

    Coding

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e4+10;
const int M=4e6+10;
const int inf=1e9;
struct node{
    int x,y,id1,id2;
}e[M],f[M];
int n,m,S,T,num,top,cnt,tot,flow,maxflow,lin[N],Next[M],ver[M],edge[M],dfn[N],low[N],ins[N],s[N],c[N],d[N],color[N];
int read(){
    char ch=getchar();int num=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){num=(num<<1)+(num<<3)+(ch^48);ch=getchar();}
    return num*f;
}
void dfs(int x,int col){
    color[x]=col;
    for(int i=lin[x];i;i=Next[i]){
        int y=ver[i];
        if(!color[y]) dfs(y,3-col);
    }
}
void add(int x,int y){ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;}
void Add(int x,int y,int z){
    ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;edge[tot]=z;
    ver[++tot]=x;Next[tot]=lin[y];lin[y]=tot;edge[tot]=0;
}
bool bfs(){
    queue<int>q;
    memset(d,0,sizeof(d));
    d[S]=1,q.push(S);
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=lin[x];i;i=Next[i]){
            int y=ver[i];
            if(!d[y]&&edge[i]){
                d[y]=d[x]+1;q.push(y);
                if(y==T) return 1;
            }
        }
    }return 0;
}
int dinic(int x,int flow){
    if(x==T) return flow;
    int rest=flow;
    for(int i=lin[x];i&&rest;i=Next[i]){
        int y=ver[i];
        if(d[y]==d[x]+1&&edge[i]){
            int k=dinic(y,min(edge[i],rest));
            if(!k) d[y]=0;
            rest-=k,edge[i]-=k,edge[i^1]+=k;
            if(!rest) return flow-rest;
        }
    }return flow-rest;
}
void tarjan(int x){
    dfn[x]=low[x]=++num;ins[x]=1,s[++top]=x;
    for(int i=lin[x];i;i=Next[i]){
        int y=ver[i];
        if(edge[i]&&!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }else if(ins[y]&&edge[i]) low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        cnt++;int y;
        do{
            y=s[top--];c[y]=cnt;ins[y]=0;
        }while(x!=y);
    }
}
bool CMP(node a,node b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
int main(){
    n=read(),m=read();
    for(int i=1;i<=m;++i){
        e[i].x=read(),e[i].y=read();
        add(e[i].x,e[i].y);add(e[i].y,e[i].x);
    }
    for(int i=1;i<=n;++i) if(!color[i]) dfs(i,1);
    S=0,T=n+1;tot=1;
    memset(ver,0,sizeof(ver));
    memset(lin,0,sizeof(lin));
    memset(Next,0,sizeof(Next));
    for(int i=1;i<=m;++i){
        int x=e[i].x,y=e[i].y;
        if(color[x]==2)Add(y,x,1);
        else Add(x,y,1);
    }
    for(int i=1;i<=n;++i){
        if(color[i]==1) Add(S,i,1);
        else if(color[i]==2) Add(i,T,1);
    }
    while(bfs()){
        while(flow=dinic(S,inf)) maxflow+=flow;
    }
    for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    int ans=0;
    for(int i=2;i<=tot;i+=2){
        int x=ver[i^1],y=ver[i];
        if(x==0||y==0||x==T||y==T)continue;
        if(edge[i]==0&&c[x]!=c[y])f[++ans].x=min(x,y),f[ans].y=max(x,y);
    }
    printf("%d\n",ans);
    sort(f+1,f+ans+1,CMP);
    for(int i=1;i<=ans;++i){
        printf("%d %d\n",f[i].x,f[i].y);
    }
    return 0;
}