AGC 029 F 题解

Description

给定 n − 1 n-1 n1 个点集 E 1 , E 2 , ⋯   , E n − 1 E_1,E_2,\cdots,E_{n-1} E1,E2,,En1,请从每个点集中选两个点连边,构成一棵树。

若不存在这样的方案,请输出 − 1 -1 1;否则请输出方案。

1 ≤ N ≤ 1 0 5 , ∑ i = 1 n − 1 ∣ E i ∣ ≤ 2 × 1 0 5 1 \le N \le 10^5,\sum_{i=1}^{n-1}{|E_i|} \le 2 \times 10^5 1N105,i=1n1Ei2×105

Solution

首先,形成一棵树的必要条件是: 所有点的入度非 0 0 0

考虑先满足这个条件。我们建立一个二分图,左边有 n − 1 n-1 n1 个点,右边有 n n n 个点。对于每一个点集 E i E_i Ei,我们将 i i i E i E_i Ei 中所有的点连边。若此二分图的最大匹配为 n − 1 n-1 n1,则我们就满足了上述条件。

我们可以通过 Dinic 在 O ( n n ) O(n \sqrt n) O(nn ) 的代价内求出最大匹配与所有在最大匹配中的边

为了方便叙述,令左侧点 i i i v i v_i vi 匹配。我们对每个左侧点 i i i,钦定第 i i i 条边的一端为 v i v_i vi,另一端为 u i u_i ui

现在关键在于构造出所有的 u i u_i ui

注意到右侧有且仅有一个点失配,考虑以此切入。令这失配的点为 t t t。我们先将 p p p 赋值为 t t t,然后执行下列步骤:
①遍历与 p p p 相邻且未被访问过的左侧点 q q q
②令 q q q 被访问过;
③令第 p p p 行的一组答案为 ( u p , v q ) (u_p,v_q) (up,vq)
④将 u u u 赋值为 v q v_q vq
⑤从①重新开始。

特别的,当在 ① 中无法找到一个合法的 q q q 时,直接宣布无解即可。

由于 Dinic 求二分图最大匹配问题的复杂度为 O ( m n ) O(m \sqrt n) O(mn ),而本题 n , m n,m n,m 同级,从而总复杂度 O ( n n ) O(n \sqrt n) O(nn )

下面给出关于正确性的简要证明。

Lemma 1

得到的答案一定是一组合法解。

Prove

不难发现,只要每行都挑选出了两个点 ( u , v ) (u,v) (u,v) 并将其相连,且最终得到的图没有环,则它一定是我们所需要的树。

考虑在构造出来的树中是否会出现环。

我们运用反证法。令存在环 a 1 → a 2 → ⋯ → a k → a 1 a_1\to a_2 \to \cdots \to a_k \to a_1 a1a2aka1

  • a 1 ≠ t a_1 \neq t a1=t
    • 不难发现,在搜索到 a k a_k ak 之前,与 a 1 , a 2 , ⋯   , a k a_1,a_2,\cdots,a_k a1,a2,,ak 匹配的左侧点都被标记为“已访问”,从而不可能从 a k a_k ak 搜到 a 1 a_1 a1 并形成环。
  • a 1 = t a_1=t a1=t
    • 由于 t t t 没有匹配的点,所以离开 t t t 之后就不可能再回来了,从而不可能形成环。

证毕。

Lemma 2

不可能将 ⌊ \lfloor 有解 ⌉ \rceil 判断为 ⌊ \lfloor 无解 ⌉ \rceil

Prove

无解共有两种情况。

第一种情况是在做二分图匹配时发现最大匹配不为 n − 1 n-1 n1。根据文章开头提出的“引理”,此时一定无解。

第二种情况是在构造解时发现无法找到满足要求的 q q q。首先,我们可以发现,此时未经过的左侧点与右侧点数量相等,无论接下来如何处理均会出现环。同时,对于此时任意一个左侧未被经过的点 r r r,与 r r r 相邻的右侧节点均未经过,从而无法进行调整。由于无法继续进行且无法调整,我们无力回天。

证毕。


综上所述,得到的答案一定合法,且不会将有解判断为无解。我们解法的正确性得到了证明。

Code

#include <bits/stdc++.h>
using namespace std;
const int maxl=200005;

int read(){
	int s=0,w=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-')  w=-w;ch=getchar();}
	while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
	return s*w;
}
int n,s,t,res,cnt=1,cur,pos,flag;
int head[maxl],mat[maxl],vis[maxl],dep[maxl];

struct Tree{int u,v;}ans[maxl];
struct edge{int nxt,to,f;}e[maxl<<2];

void add_edge(int u,int v,int flow){
	cnt++;
	e[cnt].to=v,e[cnt].f=flow,e[cnt].nxt=head[u],head[u]=cnt;
}
void Add(int u,int v,int f){add_edge(u,v,f),add_edge(v,u,0);}

bool bfs(){
	for (int i=s;i<=t;i++)  dep[i]=0;
	
	queue<int> q;
	q.push(s),dep[s]=1;
	while (!q.empty()){
		int now=q.front();q.pop();
		for (int i=head[now];i;i=e[i].nxt){
			int y=e[i].to;
			if (e[i].f && (!dep[y])){
				dep[y]=dep[now]+1;
				q.push(y);
			}
		}
	}
	return dep[t];
} 

int dfs(int now,int flow){
	if (now==t)  return flow;
	
	int Out=0;
	for (int i=head[now];i;i=e[i].nxt){
		if (flow<=0)  break;
		
		int y=e[i].to;
		if (dep[y]==dep[now]+1&&e[i].f){
			int tmp=dfs(y,min(flow,e[i].f));
			e[i].f-=tmp,e[i^1].f+=tmp;
			flow-=tmp,Out+=tmp;
		}
	}
	if (!Out)  dep[now]=-114514;
	return Out;
}

void dfs2(int now){
	for (int i=head[now];i;i=e[i].nxt){
		int y=e[i].to;
		if (y>n||y==s||vis[y])  continue;
	
		vis[y]=1,cnt++;
		ans[y].u=now-n,ans[y].v=mat[y]-n;
		
		dfs2(mat[y]);
	}
}

signed main(){
	n=read(),s=0,t=2*n+1;
	for (int i=1;i<=n;i++)  Add(i+n,t,1);
	for (int i=1;i<n;i++){
		int x=read();
		Add(s,i,1); 
		for (int j=1;j<=x;j++){
			int y=read();
			Add(i,y+n,1);
		}
	}
	while (bfs())  res+=dfs(s,1e9);

	if (res<n-1)  return cout<<-1<<endl,0;
	for (int now=1;now<n;now++){
		for (int i=head[now];i;i=e[i].nxt){
			int y=e[i].to;
			if (!e[i].f&&y>n)  mat[now]=y,vis[y]=1;
		}
	}
	for (int i=1;i<=n;i++){
		if (!vis[i+n]){
			cur=i;
			break;
		}
	}
	memset(vis,0,sizeof(vis)),cnt=0;
	dfs2(cur+n);
	
	if (cnt==n-1){
		for (int i=1;i<n;i++)  printf("%d %d\n",ans[i].u,ans[i].v);
	}
	else puts("-1");
	
	return 0;
}

应该讲得很清楚了吧 ⋯ ⋯ \cdots \cdots

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值