【Luogu3731】[HAOI2017]新型城市化(网络流,Tarjan)

【Luogu3731】[HAOI2017]新型城市化(网络流,Tarjan)

题面

洛谷
给定一张反图,保证原图能分成不超过两个团,问有多少种加上一条边的方法,使得最大团的个数至少加上\(1\)

题解

本来并不会做的,看题解第一句话就会了QwQ
对于在反图上没有边的点之间是存在一条边的。
那么考虑原图的一个团对应在反图上是什么,因为原图的团内的点两两之间有边,所以对应在反图上两两之间无边。所以原图的一个团对应着反图的一个独立集。
因为原图可以分解为不超过\(2\)个团,所以反图可以分解成一个二分图。
于是问题变成了,删去反图上的哪条边,可以让反图的最大独立集变大。
二分图最大独立集=总点数-最小点覆盖,而最小点覆盖=二分图最大匹配。
于是问题变成了哪些边必定出现在二分图的最大匹配中。
那么先跑\(Dinic\)之后用\(Tarjan\)把残余网络缩点,
必定在二分图匹配上的边就是那些满流并且连接的两点不在同一个\(scc\)内的边。
证明大概就是如果两者在同一个\(scc\)内,那么必定存在另外一条匹配边可以替换这条边。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 10100
#define MAXM 150150
#define inf 1e9
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Line{int v,next,w;}e[MAXM<<1];
int h[MAXN],cnt=2;
inline void Add(int u,int v,int w)
{
	e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
	e[cnt]=(Line){u,h[v],0};h[v]=cnt++;
}
int S,T,level[MAXN];
bool bfs()
{
	for(int i=S;i<=T;++i)level[i]=0;
	queue<int> Q;level[S]=1;Q.push(S);
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=h[u];i;i=e[i].next)
			if(e[i].w&&!level[e[i].v])
				level[e[i].v]=level[u]+1,Q.push(e[i].v);
	}
	return level[T];
}
int cur[MAXN];
int dfs(int u,int flow)
{
	if(u==T||!flow)return flow;
	int ret=0;
	for(int &i=cur[u];i;i=e[i].next)
	{
		int v=e[i].v,d;
		if(e[i].w&&level[v]==level[u]+1)
		{
			d=dfs(v,min(flow,e[i].w));
			ret+=d;flow-=d;
			e[i].w-=d;e[i^1].w+=d;
			if(!flow)break;
		}
	}
	if(!ret)level[u]=0;
	return ret;
}
int Dinic()
{
	int ret=0;
	while(bfs())
	{
		for(int i=S;i<=T;++i)cur[i]=h[i];
		ret+=dfs(S,inf);
	}
	return ret;
}
vector<pair<int,int> > Ans;
vector<int> E[MAXN];
int n,m,col[MAXN];
void pre(int u,int c){col[u]=c;for(int v:E[u])if(!col[v])pre(v,c^1);}
int dfn[MAXN],low[MAXN],tim,G[MAXN],gr;
int St[MAXN],top;bool ins[MAXN];
void Tarjan(int u)
{
	dfn[u]=low[u]=++tim;St[++top]=u;ins[u]=true;
	for(int i=h[u];i;i=e[i].next)
	{
		int v=e[i].v;if(!e[i].w)continue;
		if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);
		else if(ins[v])low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		++gr;int v;
		do{v=St[top--];G[v]=gr;ins[v]=false;}while(u!=v);
	}
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=m;++i)
	{
		int u=read(),v=read();
		E[u].push_back(v);
		E[v].push_back(u);
	}
	for(int i=1;i<=n;++i)if(!col[i])pre(i,2);
	S=0;T=n+1;
	for(int i=1;i<=n;++i)
		if(col[i]==2)
		{
			Add(S,i,1);
			for(int v:E[i])Add(i,v,1);
		}
		else Add(i,T,1);
	int ret=Dinic();
	for(int i=S;i<=T;++i)if(!dfn[i])Tarjan(i);
	for(int u=1;u<=n;++u)
		if(col[u]==2)
			for(int i=h[u];i;i=e[i].next)
			{
				int v=e[i].v;if(e[i].w)continue;
				if(v==S||v==T)continue;
				if(G[u]!=G[v])Ans.push_back(u<v?make_pair(u,v):make_pair(v,u));
			}
	sort(Ans.begin(),Ans.end());
	printf("%d\n",(int)Ans.size());
	for(auto a:Ans)printf("%d %d\n",a.first,a.second);
	return 0;
}
posted @ 2019-03-24 22:08  小蒟蒻yyb  阅读(337)  评论(3编辑  收藏  举报