zoukankan      html  css  js  c++  java
  • AtCoder AGC029F Construction of a Tree (二分图匹配)

    题目链接

    https://atcoder.jp/contests/agc029/tasks/agc029_f

    题解

    考虑如何才能构成一棵树:显然有一个必要条件是对于每个点(u)来说,整张图所有的边与除去(u)之外所有的点存在完美匹配(即考虑一张二分图左边是除了(u)之外点的集合右边是(E_i), (u)(E_i)连边当且仅当(uin E_i),该图存在完美匹配)。用Hall定理来表达就是,设(S)({ E_1,E_2,..,E_n})的任意一个子集,(N(S))表示这些(E_i)对应的点集的并集,则(|N(S)|ge |S|+1).
    实际上这个条件也是充分条件。我们用一个构造算法来证明。直接从(1)号点开始BFS或者DFS,每次(u)选择一条出边到右边的一个点(v),然后跳到右边的点的匹配点(u'). 若这两个点都没被访问过,则添加一条树边((u,u')). 上面的命题等价于这样搜索能够遍历所有的点。因为如果某一个时刻不能走到未走过的点,那么走过的左边点个数比右边点个数多(1),左边点总个数比右边点总个数多(1),故现在未遍历的右边的点的集合(T)满足(N(T)le T),这与上面的命题矛盾。而如果上面的命题不成立,显然无法搜出合法的方案。而这样搜索能遍历所有的点等价于原问题有解,故原命题等价于原问题有解。
    时间复杂度(O(sum |E_i|sqrt n)).

    注: 在这里由于时间复杂度的限制,我们只能用二分图匹配检验(1)号点是否满足去掉后有完美匹配,但是这并不代表所有点都有。我们证明了有解的充要条件是每个点去掉后都有完美匹配,也是(1)号点去掉后有完美匹配且BFS/DFS不会在遍历完所有点前终止,我们的算法是正确的。但是似乎数据里并没有这种(1)号点去掉后有完美匹配但实际上无解的情况,把下面BFS的代码中标注comment_1的那一行return 0;前面加一个assert(0);依然可以AC. 实际上这种情况完全可能出现,Hack数据如下:

    4
    4 1 2 3 4
    2 3 4
    2 3 4
    

    代码

    BFS

    #include<bits/stdc++.h>
    #define llong long long
    #define mkpr make_pair
    #define riterator reverse_iterator
    #define pii pair<int,int>
    using namespace std;
    
    inline int read()
    {
    	int x = 0,f = 1; char ch = getchar();
    	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    	return x*f;
    }
    
    const int INF = 1e7;
    
    namespace NetFlow
    {
    	const int N = 2e5+2;
    	const int M = 4e5;
    	struct Edge
    	{
    		int v,w,nxt,rev;
    	} e[(M<<1)+3];
    	int fe[N+3];
    	int te[N+3];
    	int dep[N+3];
    	int que[N+3];
    	int n,en,s,t;
    	void addedge(int u,int v,int w)
    	{
    		en++; e[en].v = v; e[en].w = w;
    		e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;
    		en++; e[en].v = u; e[en].w = 0;
    		e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;
    	}
    	bool bfs()
    	{
    		for(int i=1; i<=n; i++) dep[i] = 0;
    		int head = 1,tail = 1; que[1] = s; dep[s] = 1;
    		while(head<=tail)
    		{
    			int u = que[head]; head++;
    			for(int i=fe[u]; i; i=e[i].nxt)
    			{
    				int v = e[i].v;
    				if(e[i].w>0 && dep[v]==0)
    				{
    					dep[v] = dep[u]+1;
    					if(v==t) return true;
    					tail++; que[tail] = v;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int u,int cur)
    	{
    		if(u==t||cur==0) {return cur;}
    		int rst = cur;
    		for(int &i=te[u]; i; i=e[i].nxt)
    		{
    			int v = e[i].v;
    			if(e[i].w>0 && rst>0 && dep[v]==dep[u]+1)
    			{
    				int flow = dfs(v,min(rst,e[i].w));
    				if(flow>0)
    				{
    					e[i].w -= flow;	
    					rst -= flow;
    					e[e[i].rev].w += flow;
    					if(rst==0) {return cur;}
    				}
    			}
    		}
    		if(rst==cur) {dep[u] = -2;}
    		return cur-rst;
    	}
    	int dinic(int _n,int _s,int _t)
    	{
    		n = _n,s = _s,t = _t;
    		int ret = 0;
    		while(bfs())
    		{
    			for(int i=1; i<=n; i++) te[i] = fe[i];
    			memcpy(te,fe,sizeof(int)*(n+1));
    			ret += dfs(s,INF);
    		}
    		return ret;
    	}
    }
    using NetFlow::addedge;
    using NetFlow::dinic;
    
    const int N = 1e5;
    vector<int> adj[(N<<1)+3];
    vector<pair<int,pii> > ans;
    int mch[(N<<1)+3];
    bool vis[(N<<1)+3];
    int que[N+3];
    int n;
    
    bool bfs()
    {
    	int hd = 1,tl = 1; que[1] = 1; vis[1] = true;
    	while(hd<=tl)
    	{
    		int u = que[hd]; hd++;
    		for(int o=0; o<adj[u].size(); o++)
    		{
    			int v = adj[u][o]; if(vis[v]) continue;
    			if(vis[mch[v]]) continue;
    			que[++tl] = mch[v]; vis[v] = vis[mch[v]] = true;
    			ans.push_back(mkpr(v,mkpr(u,mch[v])));
    		}
    	}
    	if(tl<n) {return false;}
    	return true;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) addedge(1,i+2,1);
    	for(int i=n+1; i<n+n; i++) addedge(i+2,2,1);
    	for(int i=1; i<n; i++)
    	{
    		int sz; scanf("%d",&sz);
    		while(sz--)
    		{
    			int x; scanf("%d",&x); adj[i+n].push_back(x); adj[x].push_back(i+n);
    			if(x!=1) {addedge(x+2,i+n+2,1);}
    		}
    	}
    	if(dinic(n+n+1,1,2)<n-1) {puts("-1"); return 0;}
    	for(int u=3; u<=n+2; u++)
    	{
    		for(int i=NetFlow::fe[u]; i; i=NetFlow::e[i].nxt)
    		{
    			int v = NetFlow::e[i].v; if(v<=n+2) continue;
    			if(NetFlow::e[i].w==0)
    			{
    				mch[u-2] = v-2,mch[v-2] = u-2;
    				break;
    			}
    		}
    	}
    //	printf("match: "); for(int i=1; i<=n+n-1; i++) printf("%d ",mch[i]); puts("");
    	if(!bfs()) {puts("-1"); return 0;} //comment_1
    	sort(ans.begin(),ans.end());
    	for(int i=0; i<ans.size(); i++) printf("%d %d
    ",ans[i].second.first,ans[i].second.second);
    	return 0;
    }
    

    DFS

    #include<bits/stdc++.h>
    #define llong long long
    #define mkpr make_pair
    #define riterator reverse_iterator
    #define pii pair<int,int>
    using namespace std;
    
    inline int read()
    {
    	int x = 0,f = 1; char ch = getchar();
    	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    	return x*f;
    }
    
    const int INF = 1e7;
    
    namespace NetFlow
    {
    	const int N = 2e5+2;
    	const int M = 4e5;
    	struct Edge
    	{
    		int v,w,nxt,rev;
    	} e[(M<<1)+3];
    	int fe[N+3];
    	int te[N+3];
    	int dep[N+3];
    	int que[N+3];
    	int n,en,s,t;
    	void addedge(int u,int v,int w)
    	{
    		en++; e[en].v = v; e[en].w = w;
    		e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;
    		en++; e[en].v = u; e[en].w = 0;
    		e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;
    	}
    	bool bfs()
    	{
    		for(int i=1; i<=n; i++) dep[i] = 0;
    		int head = 1,tail = 1; que[1] = s; dep[s] = 1;
    		while(head<=tail)
    		{
    			int u = que[head]; head++;
    			for(int i=fe[u]; i; i=e[i].nxt)
    			{
    				int v = e[i].v;
    				if(e[i].w>0 && dep[v]==0)
    				{
    					dep[v] = dep[u]+1;
    					if(v==t) return true;
    					tail++; que[tail] = v;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int u,int cur)
    	{
    		if(u==t||cur==0) {return cur;}
    		int rst = cur;
    		for(int &i=te[u]; i; i=e[i].nxt)
    		{
    			int v = e[i].v;
    			if(e[i].w>0 && rst>0 && dep[v]==dep[u]+1)
    			{
    				int flow = dfs(v,min(rst,e[i].w));
    				if(flow>0)
    				{
    					e[i].w -= flow;	
    					rst -= flow;
    					e[e[i].rev].w += flow;
    					if(rst==0) {return cur;}
    				}
    			}
    		}
    		if(rst==cur) {dep[u] = -2;}
    		return cur-rst;
    	}
    	int dinic(int _n,int _s,int _t)
    	{
    		n = _n,s = _s,t = _t;
    		int ret = 0;
    		while(bfs())
    		{
    			for(int i=1; i<=n; i++) te[i] = fe[i];
    			memcpy(te,fe,sizeof(int)*(n+1));
    			ret += dfs(s,INF);
    		}
    		return ret;
    	}
    }
    using NetFlow::addedge;
    using NetFlow::dinic;
    
    const int N = 1e5;
    vector<int> adj[(N<<1)+3];
    vector<pair<int,pii> > ans;
    int mch[(N<<1)+3];
    bool vis[(N<<1)+3];
    int n;
    
    void dfs(int u)
    {
    	for(int o=0; o<adj[u].size(); o++)
    	{
    		int v = adj[u][o]; if(vis[v]) continue;
    		if(vis[mch[v]]) continue;
    		vis[v] = vis[mch[v]] = true; ans.push_back(mkpr(v,mkpr(u,mch[v])));
    		dfs(mch[v]);
    	}
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) addedge(1,i+2,1);
    	for(int i=n+1; i<n+n; i++) addedge(i+2,2,1);
    	for(int i=1; i<n; i++)
    	{
    		int sz; scanf("%d",&sz);
    		while(sz--)
    		{
    			int x; scanf("%d",&x); adj[i+n].push_back(x); adj[x].push_back(i+n);
    			if(x!=1) {addedge(x+2,i+n+2,1);}
    		}
    	}
    	if(dinic(n+n+1,1,2)<n-1) {puts("-1"); return 0;}
    	for(int u=3; u<=n+2; u++)
    	{
    		for(int i=NetFlow::fe[u]; i; i=NetFlow::e[i].nxt)
    		{
    			int v = NetFlow::e[i].v; if(v<=n+2) continue;
    			if(NetFlow::e[i].w==0)
    			{
    				mch[u-2] = v-2,mch[v-2] = u-2;
    				break;
    			}
    		}
    	}
    //	printf("match: "); for(int i=1; i<=n+n-1; i++) printf("%d ",mch[i]); puts("");
    	vis[1] = true; dfs(1);
    	if(ans.size()<n-1) {puts("-1"); return 0;}
    	sort(ans.begin(),ans.end());
    	for(int i=0; i<ans.size(); i++) printf("%d %d
    ",ans[i].second.first,ans[i].second.second);
    	return 0;
    }
    
  • 相关阅读:
    POJ3320 Jessica's Reading Problem
    POJ3320 Jessica's Reading Problem
    CodeForces 813B The Golden Age
    CodeForces 813B The Golden Age
    An impassioned circulation of affection CodeForces
    An impassioned circulation of affection CodeForces
    Codeforces Round #444 (Div. 2) B. Cubes for Masha
    2013=7=21 进制转换
    2013=7=15
    2013=7=14
  • 原文地址:https://www.cnblogs.com/suncongbo/p/12270864.html
Copyright © 2011-2022 走看看