P3731 [HAOI2017]新型城市化 网络流+TARJAN

题意:

戳这里

分析:

据说是典型题,我好菜还是不会

题意转化一下,相当于每次找一条边,使得加上这条边之后,原图的最大团点数至少+1 , 乍一看没什么想法,但是题目又给出了一个条件,那就是保证原图最多可以被划分为两个最大团 ,又因为原图的最大团=补图的最大独立集=总点数-最小点覆盖数=总点数-补图最大匹配,所以我们要求的东西就转化为和补图最大匹配有关,而原图的补图就是一张二分图,所以我们要求的就是在这张二分图上,删掉哪条边会使得最大匹配至少减少 (1) ,即二分图的最大匹配必经边

好像是一种经典题,没做过/kk

具体做法就是,先跑一遍最大流,然后对于残量网络跑 (tarjan) ,枚举每一条满流的边,看一下它连接的两个点是不是在一个强连通分量里面

感性理解一下就是,对于 (u o v) 这条边,如果 (u,v) 在一个(scc) 里面,那么残量网络里面,一定有一条或几条边可以代替这条满流的边,所以它不是必经边

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 1e4+5;
	const int maxm = 3e5+5;
	const int inf = 0x3f3f3f;
	int n,m,st,top,cnt=1,ed,idx,scc;
	int head[maxn],dep[maxn],dfn[maxn],low[maxn],sta[maxn],col[maxn],bel[maxn];
	bool vis[maxn];
	vector<int> g[maxn];
	vector<pii> ans;
	queue<int> q;
	struct edge
	{
		int to,nxt,w;
	}e[maxm];
	
	void add(int u,int v,int w)
	{
		e[++cnt].to=v;
		e[cnt].w=w;
		e[cnt].nxt=head[u];
		head[u]=cnt;
	}
	
	void add_edge(int u,int v,int w)
	{
		add(u,v,w);add(v,u,0);
	}
	
	void paint(int u,int c)
	{
		col[u]=c;
		for(auto v:g[u]) if(!col[v]) paint(v,c*-1);
	}
	
	bool bfs()
	{
		for(int i=st;i<=ed;i++) dep[i]=-1;
		q.push(st);dep[st]=0;
		while(!q.empty())
		{
			int u=q.front();q.pop();
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				if(e[i].w&&dep[v]==-1)
				{
					dep[v]=dep[u]+1;
					q.push(v);
				}
			}
		}
		return dep[ed]!=-1;
	}
	
	int dfs(int u,int flow)
	{
		if(u==ed) return flow;
		int used=0,w;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(e[i].w&&dep[v]==dep[u]+1)
			{
				w=dfs(v,min(e[i].w,flow-used));
				e[i].w-=w;
				e[i^1].w+=w;
				used+=w;
				if(used==flow) return used;
			}
		}
		if(!used) dep[u]=-1;
		return used;
	}
	
	void dinic()
	{
		while(bfs()) dfs(st,inf);
	}
	
	void tarjan(int u)
	{
		dfn[u]=low[u]=++idx;
		sta[++top]=u;
		vis[u]=true;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(!e[i].w) continue;
			if(!dfn[v])
			{
				tarjan(v);
				low[u]=min(low[u],low[v]);
			}
			else if(vis[v]) low[u]=min(low[u],dfn[v]);
		}
		if(low[u]==dfn[u])
		{
			scc++;
			do
			{
				vis[sta[top]]=false;
				bel[sta[top]]=scc;
			}
			while(sta[top--]!=u);
		}
	}
	
	void work()
	{
		int a,b;
		n=read();m=read();st=0;ed=n+1;
		for(int i=1;i<=m;i++)
		{
			a=read();b=read();
			g[a].pb(b);g[b].pb(a);
		}
		for(int i=1;i<=n;i++) if(!col[i]) paint(i,1);
		for(int i=1;i<=n;i++)
		{
			if(col[i]==1)
			{
				add_edge(st,i,1);
				for(auto v:g[i]) add_edge(i,v,1);
			}
			else add_edge(i,ed,1);
		}
		dinic();
		for(int i=st;i<=ed;i++) if(!dfn[i]) tarjan(i);
		for(int u=1;u<=n;u++)
		{
			if(col[u]==1)
			{
				for(int i=head[u];i;i=e[i].nxt)
				{
					int v=e[i].to;
					if(col[v]==-1&&!e[i].w&&bel[u]!=bel[v]) ans.pb(u<v?mk(u,v):mk(v,u));
				}
			}
		}
		sort(ans.begin(),ans.end());
		printf("%d
",(int)ans.size());
		for(auto i:ans) printf("%d %d
",i.fir,i.sec);
	}

}

int main()
{
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/14265323.html