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

HAOI2017

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

Solution

  • 题目给定 m m 对点对,这 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;
}

猜你喜欢

转载自blog.csdn.net/qq_39759315/article/details/88782505