【构造题 贪心】cf1041E. Tree Reconstruction

本文深入探讨了树重构问题,分析了如何通过给定的边集信息判断并构造一棵树,使其满足特定条件。介绍了菊花图构造和链构造两种方法,详细解释了其背后的逻辑和实现步骤。

比赛时候还是太慢了……要是能做快点就能上分了

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 n1n−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 (2n10002≤n≤1000) — the number of vertices in the tree.

Each of the next n1n−1 lines contains two integers aiai and bibi each (1ai<bin1≤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 n1n−1 lines. Each of the last n1n−1 lines should contain two integers xixi and yiyi (1xi,yin1≤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.


 

题目大意

现有一棵树。用$n-1$个二元组(x,y)描述树上每条边,表示:删去这条边后,两个连通块内分别最大的编号。

问是否存在一颗符合描述的树。

题目分析

菊花图构造

注意到树被分为两个连通块后,产生的二元组(x,y)中必定有一个元素为$n$.

那么,有多少个二元组$(x,y)(y=n)$就说明有多少条边满足:$[x+1,n]$这些点和$x$点分别在被割断的两块。

树有一个性质:两点间的路径唯一。那么为了割断$x$和$[x+1]...n$,这两个连通块只能有唯一路径,并且路径上的点都必须小于$x$。于是这里有了一种对于单个点$x$的构造方法。那么其他点应该如何考虑?是应该在做出来的路径上分叉还是新开一条?

构造题可以说分为两类:唯一解和多解。这种多解的题,当然选一种最简洁的构造方法。事实上这样对于单点做下去,构造菊花图的方式就是合法的。

我们对于构造菊花图的担忧主要在于,要是每次都新开一条路径,会不会浪费了一些点?换而言之,原先的路径上能不能够共用一些点?

然而,共用路径上的边无论如何只会贡献一种二元组。如果共用,为了达到给定的二元组数量,也只能在原先路径上插上一条可独立的完整路径————也就是说相当于没有共用。

 

 1 #include<bits/stdc++.h>
 2 const int maxn = 1003;
 3 
 4 int n;
 5 bool used[maxn];
 6 int mp[maxn][maxn];
 7 int edgeTot,edges[maxn<<1],nxt[maxn<<1],head[maxn];
 8 
 9 int read()
10 {
11     char ch = getchar();
12     int num = 0;
13     bool fl = 0;
14     for (; !isdigit(ch); ch=getchar())
15         if (ch=='-') fl = 1;
16     for (; isdigit(ch); ch=getchar())
17         num = (num<<1)+(num<<3)+ch-48;
18     if (fl) num = -num;
19     return num;
20 }
21 void errorDown()
22 {
23     puts("NO");
24     exit(0);
25 }
26 void addedge(int u, int v)
27 {
28     edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
29     edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
30 }
31 void dfs(int x, int fa)
32 {
33     for (int i=head[x]; i!=-1; i=nxt[i])
34     {
35         int v = edges[i];
36         if (v!=fa) printf("%d %d\n",x,v), dfs(v, x);
37     }
38 }
39 int main()
40 {
41     n = read();
42     memset(head, -1, sizeof head);
43     for (int i=1; i<n; i++)
44     {
45         int u = read(), v = read();
46         if (u > v) std::swap(u, v);
47         if (v!=n) errorDown();
48         mp[u][v]++;
49     }
50     for (int i=1; i<n; i++)
51         if (mp[i][n]){
52             if (i < mp[i][n]) errorDown();
53             used[i] = 1;
54             int lst = i, cnt = 0;
55             for (int j=1; j<i&&cnt<mp[i][n]-1; j++)
56                 if (!used[j]){
57                     used[j] = 1;
58                     addedge(lst, j);
59                     lst = j;
60                     cnt++;
61                 }
62             if (cnt!=mp[i][n]-1) errorDown();
63             addedge(lst, n);
64         }
65     puts("YES");
66     dfs(1, 0);
67     return 0;
68 }

 

链构造

上一个做法的最后一段话并不是说不能构造出合法链。事实上可发现,刻意把小的节点接在大节点后也是可以的。

这里介绍一种非常巧妙的链构造:将$a_i$视作前缀最大值,由此构造一条链。

具体的证明和代码见  题解 CF1041E 【Tree Reconstruction】

 

 

 

END

转载于:https://www.cnblogs.com/antiquality/p/9739744.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值