AGC029F[网络流][二分图]

前言

难的不是网络流,难的是想到这么做,难的是证明

题目

Atcoder
大意
给你一个数 n n n n − 1 n-1 n1 个集合 E i E_i Ei ,其中 E i ∈ { 1 , 2 , . . . , n } E_i\in\{1,2,...,n\} Ei{1,2,...,n}
现在从每个集合 E i E_i Ei 中选出一对数 ( u i , v i ) (u_i,v_i) (ui,vi) 组成一条边,使得选出 n − 1 n-1 n1 条边构成一颗树,输出选择方案,没有输出 − 1 -1 1
2 ≤ n ≤ 1 0 5 2\le n \le10^5 2n105
∣ E i ∣ ≥ 2 , ∑ ∣ E i ∣ ≤ 2 ∗ 1 0 5 |E_i|\ge2,\sum|E_i|\le2*10^5 Ei2,Ei2105

思路

先将做法再说证明,因为并不知道怎么想出这一做法的…

做法

考虑以 1 1 1 为根,那么每条边可以表示成 ( f a u , u ) (fa_u,u) (fau,u),先不考虑 f a u fa_u fau 只考虑 u u u 的选择,那么 n − 1 n-1 n1 个集合对应 n − 1 n-1 n1 个数,跑最大匹配,如果匹配数 < n − 1 <n-1 <n1 就无解
E i E_i Ei 匹配 x i x_i xi
接下来构造每个数 f a u fa_u fau,采用从 1 1 1 出发的 B F S BFS BFS,设当前已经变遍历点集合为 S S S 对于 E i E_i Ei E i ∩ S ≠ ϕ E_i\cap S\not=\phi EiS=ϕ 则将 x i x_i xi 加入 S S S 不断更新即可,若最后无法遍历 n n n 个点,输出无解

证明

考虑如果出现第二种无解情况,即出现分层的情况,设分层时遍历 E i E_i Ei 的集合为 S S S,未遍历的 E i E_i Ei 的集合为 T T T ,现在尝试更换匹配方式看是否可能有解:

  1. S S S T T T 中交换
    发现这是不可能的,因为假设对两个 E 1 , E 2 E_1,E_2 E1,E2 交换两个编号为 x 1 , x 2 x_1,x_2 x1,x2 的,那么发现交换前后 E 1 , E 2 E_1,E_2 E1,E2 都是连通的,矛盾
  2. S S S S S S 中交换
    并不会影响 T T T,还是分层
  3. T T T T T T 中交换
    并不会影响 S S S,还是分层

Besides

注意用 D i n i c Dinic Dinic 跑最大匹配是 m n m\sqrt{n} mn

代码

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline int read() {
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 200000
#define MAXM 500000
#define INF 0x3f3f3f3f
struct Edge{
	int nxt,v,cap;
}edge[2*MAXM+5];
int ecnt,head[MAXN+5],cur[MAXN+5];
void Init(){
	ecnt=-1,memset(head,-1,sizeof(head));
	return ;
}
void Addedge(int u,int v,int cap){
	//printf("%d %d %d\n",u,v,cap);
	edge[++ecnt]=(Edge){head[u],v,cap},head[u]=ecnt;
	edge[++ecnt]=(Edge){head[v],u,0},head[v]=ecnt;
	return ;
}
int N,S,T;
int dep[MAXN+5];
bool BFS(){
	queue<int> Q;
	for(int i=0;i<=N;i++)
		dep[i]=INF;
	dep[S]=0,Q.push(S);
	while(!Q.empty()){
		int u=Q.front();Q.pop();
		for(int i=head[u];~i;i=edge[i].nxt){
			int v=edge[i].v,cap=edge[i].cap;
			if(dep[v]==INF&&cap)
				dep[v]=dep[u]+1,Q.push(v);
		}
	}
	return dep[T]<INF;
}
int DFS(int u,int aug){
	if(u==T) return aug;
	int flow=0,f;
	for(int &i=cur[u];~i;i=edge[i].nxt){
		int v=edge[i].v,cap=edge[i].cap;
		if(dep[v]==dep[u]+1&&cap&&(f=DFS(v,min(aug,cap)))){
			aug-=f,flow+=f;
			edge[i].cap-=f,edge[i^1].cap+=f;
			if(!aug) break;
		}
	}
	return flow;
}
int Dinic(){
	int Max_Flow=0;
	while(BFS())
		memcpy(cur,head,sizeof(head)),Max_Flow+=DFS(S,INF);
	return Max_Flow;
}
queue<int> Q;
int ma[MAXN+5];
bool vis[MAXN+5];
vector<int> G[MAXN+5];
int cho1[MAXN+5],cho2[MAXN+5];
int main(){
	Init();
	int n=read();
	N=2*n+2,S=2*n+1,T=2*n+2;
	for(int i=1;i<n;i++)
		Addedge(S,i,1);
	for(int i=2;i<=n;i++)	
		Addedge(i+n-1,T,1);
	for(int i=1;i<n;i++){
		int c=read();
		for(int j=1;j<=c;j++){
			int w=read();
			G[w].push_back(i);
			if(w!=1)
				Addedge(i,w+n-1,1);
		}
	}
	int ans=Dinic();
	if(ans!=n-1)
		puts("-1"),exit(0);
	for(int s=1;s<n;s++)
		for(int i=head[s];~i;i=edge[i].nxt)
			if(!edge[i].cap)
				ma[s]=edge[i].v-(n-1);
	Q.push(1);
	int cnt=0;
	while(!Q.empty()){
		cnt++;
		int u=Q.front();Q.pop();
		for(int i=0;i<(int)G[u].size();i++){
			int s=G[u][i];
			if(vis[s]) continue;
			vis[s]=1,Q.push(ma[s]);
			cho1[s]=u,cho2[s]=ma[s];
		}
	}
	if(cnt!=n)
		puts("-1"),exit(0);
	for(int i=1;i<n;i++)
		printf("%d %d\n",cho1[i],cho2[i]);
	return 0;
}

感悟

给我最大启发是 集合对于边的两个点选择确定根后转化为对一个点的选择+妙妙的证明转化成最大匹配问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值