【树上问题】Tree Reconstruction【Codeforces Round #509 (Div. 2)】

                                              E. Tree Reconstruction

Decription:

      Monocarp has drawn a tree (an undirected connected acyclic graph) and then has given each vertex an index. All indices are distinct numbers from 11 to nn. For every edge ee of this tree, Monocarp has written two numbers: the maximum indices of the vertices of the two components formed if the edge ee (and only this edge) is erased from the tree.

      Monocarp has given you a list of n−1n−1 pairs of numbers. He wants you to provide an example of a tree that will produce the said list if this tree exists. If such tree does not exist, say so.

Input:

      The first line contains one integer nn (2≤n≤10002≤n≤1000) — the number of vertices in the tree.

      Each of the next n−1n−1 lines contains two integers aiai and bibi each (1≤ai<bi≤n1≤ai<bi≤n) — the maximal indices of vertices in the components formed if the ii-th edge is removed.

Output:

      If there is no such tree that can produce the given list of pairs, print "NO" (without quotes).

    Otherwise print "YES" (without quotes) in the first line and the edges of the tree in the next n−1n−1 lines. Each of the last n−1n−1 lines should contain two integers xixi and yiyi (1≤xi,yi≤n1≤xi,yi≤n) — vertices connected by an edge.

Note:

      The numeration of edges doesn't matter for this task. Your solution will be considered correct if your tree produces the same pairs as given in the input file (possibly reordered). That means that you can print the edges of the tree you reconstructed in any order.

题意:

      给你颗树,然后将每个点按照1-n进行编号,然后依次删去每条边,删完之后再恢复,每次删完边之后就会出现两个连通块,两个连通块中各自节点编号的最大值就是该边的的两个数值。

      题目给出删去每一条边之后,该条边的两个数值。

      现在让你构造一颗树满足该题题意,如果可以构造,则输出yes,并且表示出该树的 n-1 条边,否则输出 no。

思路:

      每条边给出x、y,x < y,则y必定为 n ,因此需要记录每个 x 出现的次数。

      我们以下述这种情况进行举例。

i 1 2 3 4 5 6 7

cnt[i] 1 0 2 0 0 0 4

      此处 n = 8,可以发现对于一个 i 来说,假如 cnt[i] = k,则代表 i 在第k层,且 i 是一个叶子节点,并且 i 前面的节点编号全都比 i 小,而且根节点是 n ,并且 i 节点前面的节点 cnt 均为0.

      由此可以发现,sum[i] = cnt[0-i]累加,sum[i] <= i,否则一定无法构造出一颗树。

      判断完能不能构造出一个树之后,我们就可以对树进行构造。

      策略是从n-1到1进行倒序遍历,对于每个cnt[i] != 0,则向前找cnt[i]-1个0与i点构成一条链即可。

      复杂度O(n^2).

总结:

      树上的问题做了这么多,可以发现,树上问题除了那些使用各种数据结构的变态题之外,一般都是些找规律的贪心题,尤其是cf div2上几乎所有树上问题都是贪心问题,重点在于列出情况进行找规律。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 1000+100;

int n,cnt[N];
int vis[N];

int main()
{
	scanf("%d",&n);
	rep(i,1,n-1)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(y != n){
			printf("NO\n");
			return 0;
		}
		cnt[x]++;
	}
	int sum = 0;
	rep(i,1,n-1)
	{
		sum += cnt[i];
		if(sum > i){
			printf("NO\n");
			return 0;	
		}
	}
	printf("YES\n");
	rep(i,1,n-1)
	{
		if(vis[i] || !cnt[i]) continue;
		int k = cnt[i];
		int pos = i, tmp = i;
		while(k)
		{
			if(k == 1){
				printf("%d %d\n",tmp,n);
				k--;
				break;
			}
			pos--;
			if(vis[pos] || cnt[pos]) continue;
			printf("%d %d\n",tmp,pos);
			tmp = pos;
			k--, vis[pos] = 1;
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82807789