2020牛客暑期多校 第二场 Cover the Tree (DFS,证明)

题目链接: https://ac.nowcoder.com/acm/contest/5667/C

题目描述

Given an unrooted tree, you should choose the minimum number of chains that all edges in the tree are covered by at least one chain. Print the minimum number and one solution. If there are multiple solutions, print any of them.

输入描述:

The first line contains one integer n ( 1 ≤ n ≤ 2 × 1 0 5 1≤n≤2×10^5 1n2×105)n~( 1 ≤ n ≤ 2 × 1 0 5 1\leq n \leq 2\times10^5 1n2×105)n ( 1 ≤ n ≤ 2 × 1 0 5 1≤n≤2×10^5 1n2×105), denoting the size of the tree.
Following n−1{n-1}n−1 lines each contains two integers u , v ( 1 ≤ u < v ≤ n ) u , v   ( 1 ≤ u < v ≤ n ) u , v ( 1 ≤ u < v ≤ n ) u,v (1≤u<v≤n)u,v~(1\leq u < v \leq n)u,v (1≤u<v≤n) u,v(1u<vn)u,v (1u<vn)u,v(1u<vn), denoting an edge in the tree.
It’s guaranteed that the given graph forms a tree.

输出描述:

The first line contains one integer k, denoting the minimum number of chains to cover all edges.
Following k lines each contains two integers u , v ( 1 ≤ u , v ≤ n ) u , v   ( 1 ≤ u , v ≤ n ) u , v ( 1 ≤ u , v ≤ n ) u,v (1≤u,v≤n)u,v~(1\leq u,v \leq n)u,v (1≤u,v≤n) u,v(1u,vn)u,v (1u,vn)u,v(1u,vn), denoting a chain you have chosen. Here u=v is permitted, which covers no edge.

示例1

输入

5
1 2
1 3
2 4
2 5

输出

2
2 3
4 5

分析

当n小于等于2时,可以容易求出答案。

当n大于2时,任选一个度大于1的节点可以通过一遍DFS求出叶子节点总数S和对应的DFS序列a[]。我们按照(a[i],a[i+(S+1)/2])的方式配对链。特别的,对于S为奇数的情况,我们额外添加一条从a[(S+1)/2]到根节点的链。最终效果类似于:

下面简要证明:

对于任意一个有子节点或子树的节点x,要保证它下面的点至少有一个能链接到x的父节点上。运用反证法,假设x的所有子节点范围为[L,R]都不链接到x的父节点上,即这些子节点都只在相互之间进行链接,即左端点一定链接在右端点及以内,有则L+S/2<=R,即x子节点覆盖范围至少为S/2。但当范围这么大时,一定有外面的点会链接进来,即一定会有链经过x的父节点,与假设矛盾,所以假设不成立。得证按我们的规则链接必会经过父节点。对每个节点都套用这个结论,那么就能覆盖整棵树了。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int a[MAXN];
int s;
int n;
vector<int> g[MAXN];
void init(){
	s=1;
	for(int i=1;i<=n;i++){
		g[i].clear();
	}
}
void dfs(int u,int fa){
	if(g[u].size()==1){
		a[s]=u;
		s++;
	}
	else {
		int len=g[u].size();
		for(int i=0;i<len;i++){
			int v=g[u][i];
			if(v==fa) continue;
			dfs(v,u);
		}
	}
}
int main(){
	int root;
	ios::sync_with_stdio(0);
	cin>>n;
	init();
	for(int i=0;i<n-1;i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	if(n==1){
		cout<<1<<"\n";
		cout<<1<<" "<<1;
		return 0;
	}
	else if(n==2){
		cout<<1<<"\n";
		cout<<1<<" "<<2;
		return 0;
	}
	else {
		for(int i=1;i<=n;i++){
			if(g[i].size()>1){
				root=i;
				dfs(i,i);
				break;
			}
		}
		cout<<(s/2)<<"\n";
		for(int i=1;i<s/2;i++){
			if(s%2==1&&i==s/2) continue;
			cout<<a[i]<<" "<<a[i+s/2]<<"\n";
		}
		if((s-1)%2==1){
			cout<<a[s/2]<<" "<<root;
		}
		else {
			cout<<a[s/2]<<" "<<a[s-1];
		}
		return 0;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值