「清华集训 2017」小 Y 和二叉树

「清华集训 2017」小 Y 和二叉树

原题数据好像没有卡这个情况

1
2
3
4
5
6
5
1 2
2 1 3
3 2 4 5
1 3
1 3

输出是

1
1 2 3 4 5

首先考虑一个 \(O(n^2)\) 的暴力:

枚举一个点为根,向下展开树,此时只需要决策左儿子和右儿子的顺序

当两个子树都存在时,由于两个子树包含的元素不同,所以可以直接把 两个子树序列首较小 (显然不会出现相同的情况) 的一个放在前面即可

实际上我们可以发现,这样得到的序列第一个元素必然是 编号最小的不同时包含左右儿子 的结点

不妨称固定根之后,这样的结点为叶子

\[ \ \]

显然的性质:任何一个度数 \(\leq 2\) 的点可以作为答案序列的第一个点

设原树上最小的 \(\leq 2\) 的点为 \(root\) ,接下来对于 \(root\) 的不同情况讨论,要在强制 \(root\) 为序列首的情况下,求得最小的序列

不妨先预处理出结点 \(u\) 子树里最小的叶子 \(mi_u\)

1.没有相邻结点,结束

2.有两个相邻结点,此时要使自己为序列首,必然有一个结点是自己的父亲,有一个结点是自己的右儿子

右儿子会被先遍历到,所以可以直接考虑比较两个相邻结点 作为 右儿子时谁的序列首 较小

即比较两个子树中最小的叶子即可

3.只有一个相邻结点,设其为 \(v\)

此时要使得自己为序列第一个,只有两种可能,此时同样可以考虑比较序列首元素

3-1.让相邻结点作为自己的父亲,此时下一个元素一定是 \(v\)

3-2.让相邻结点作为自己的右儿子,此时下一个元素一定是 \(mi_v\)

如果 \(v\ne mi_v\) ,显然好决策

\(v=mi_v\) 时,必然满足 \(v\) 是一个叶子,此时如果将 \(v\) 放在父亲上, \(v\) 的另一个相邻结点(如果存在)

可以放在 \(v\) 的父亲或者是 \(v\) 的右子树,如果放在 \(v\) 的右子树,那么这种情况与 \(v\) 被放在 \(u\) 的子树等价

也就是说,把 \(v\) 放在父亲可以决策出的序列情况,包含了把 \(v\) 放在右儿子的情况

所以这种情况也应当把 \(v\) 放在父亲上

实现上,不妨用两个 \(dfs\) 处理最后的决策,一个强制当前结点 \(u\) 为序列首,一个求出 \(u\) 子树的最优方案

在代码里就是 \(Solve,dfs\_get\)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include<bits/stdc++.h>
using namespace std;

#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }

char IO;
int rd(){
int s=0,f=0;
while(!isdigit(IO=getchar())) if(IO=='-') f=1;
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=1e6+10;

int n;
int c[N],s[N][3];
int mi[N];

int rt=1e9;
void dfs(int u,int f) {
mi[u]=1e9;
int cnt=0;
rep(i,0,c[u]-1) if(s[u][i]!=f) {
int v=s[u][i]; cnt++;
dfs(v,u),cmin(mi[u],mi[v]);
}
if(cnt<=1) cmin(mi[u],u);
}

int vis[N];
void dfs_get(int u) {
vis[u]=1;
int a=-1,b=-1;
rep(i,0,c[u]-1) if(!vis[s[u][i]]) {
int v=s[u][i];
if(~a) b=v;
else a=v;
}
if(a==-1) printf("%d ",u);
else if(b==-1) {
if(mi[a]<u) dfs_get(a),printf("%d ",u);
else printf("%d ",u),dfs_get(a);
} else {
if(mi[a]>mi[b]) swap(a,b);
dfs_get(a),printf("%d ",u),dfs_get(b);
}
}

void Solve(int u) {
int cnt=0;
rep(i,0,c[u]-1) if(!vis[s[u][i]]) cnt++;
vis[u]=1,printf("%d ",u);
if(cnt==1) {
rep(i,0,c[u]-1) if(!vis[s[u][i]]) {
int v=s[u][i];
if(v>mi[v]) dfs_get(s[u][i]);
else Solve(v);
}
} else if(cnt==2) {
int a=-1,b=-1;
rep(i,0,c[u]-1) if(!vis[s[u][i]]) {
int v=s[u][i];
if(~a) b=v;
else a=v;
}
if(mi[a]>mi[b]) swap(a,b);
dfs_get(a),Solve(b);
}
}

int main(){
//freopen("binary.in","r",stdin),freopen("binary.out","w",stdout);
n=rd();
if(n==1) return puts("1"),0;
rep(i,1,n) {
c[i]=rd();
if(c[i]<=2) cmin(rt,i);
rep(j,0,c[i]-1) s[i][j]=rd();
}
dfs(rt,0);
Solve(rt);
}