zoukankan      html  css  js  c++  java
  • 【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
    ",(int)Ans.size());
    	for(auto a:Ans)printf("%d %d
    ",a.first,a.second);
    	return 0;
    }
    
  • 相关阅读:
    JAVA的显式锁
    JAVA线程池
    多线程中的各种锁
    《深入理解JAVA虚拟机》第三版 第七,八章 要点总结
    《深入理解JAVA虚拟机》第三版 第六章 要点总结
    JVM垃圾收集器总结
    《深入理解JAVA虚拟机》第三版 第二,三章 要点总结
    Map接口的实现类
    博客收藏列表
    毕设开发日志2017-12-28 完成!
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10590848.html
Copyright © 2011-2022 走看看