P4006 小 Y 和二叉树
题目链接
乱搞.
一颗二叉树, 每个节点的度数(d_x <= 3).
可以发现, 可以作为开头节点的点一定是度数小于等于2的.
然后我们找到编号最小, 度数小于等于2的点(s), 把它作为根, 算出(min[x]).
(min[x])表示在以(s)为根的二叉树中, 节点(x)的子树内的可以作为这一段开头结点的编号最小的点.
对于一个点(x), 它由(fa)走过来, 那么这个点可能为(fa)的右儿子, 也可能为(fa)点的父亲.
对于第一种情况, (x)要往下分左,右儿子(如果有两条出边的话), 第二种情况就是一条边通向父亲, 另一条边通向右儿子. 具体看代码吧.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 1e6 + 5;
int n, s, cnt;
int d[N], f[N], head[N];
struct edge { int to, nxt; } e[N << 1];
void add(int x, int y) {
e[++ cnt].nxt = head[x]; head[x] = cnt; e[cnt].to = y;
}
void get_tree(int x, int fa) {
f[x] = n + 1; int res = 0;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == fa) continue ;
get_tree(y, x); f[x] = min(f[x], f[y]); res ++;
}
if(res < 2) f[x] = min(f[x], x);
}
void make_ans(int x, int fa, int p) { //p = 0代表上面说的情况1, p = 1代表上面说的情况二
d[x] --;
if(!d[x]) { printf("%d ", x); return ; }
if(p) {
printf("%d ", x);
if(d[x] == 1) {
int t;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == fa) continue ; t = y;
}
make_ans(t, x, f[t] == t); //如果说f[t] = t, 说明这个点作为父亲优先输出是更优的
}
else {
int t1, t2;
for(int i = head[x]; i ; i= e[i].nxt) {
int y = e[i].to; if(y == fa) continue ; t2 = t1; t1 = y;
}
if(f[t1] < f[t2]) make_ans(t1, x, 0), make_ans(t2, x, 1);
else make_ans(t2, x, 0), make_ans(t1, x, 1);
}
}
else {
if(d[x] == 1) {
int t;
for(int i = head[x]; i ; i = e[i].nxt) {
int y = e[i].to; if(y == fa) continue ; t = y;
}
if(f[t] < x) {
make_ans(t, x, 0); printf("%d ", x); //t作为左子树
}
else {
printf("%d ", x); make_ans(t, x, 0); //t作为右子树
}
}
else {
int t1, t2;
for(int i = head[x]; i ; i= e[i].nxt) {
int y = e[i].to; if(y == fa) continue ; t2 = t1; t1 = y;
}
if(f[t1] < f[t2]) {
make_ans(t1, x, 0);
printf("%d ", x);
make_ans(t2, x, 0);
}
else {
make_ans(t2, x, 0);
printf("%d ", x);
make_ans(t1, x, 0);
}
}
}
}
int main() {
n = read(); s = n + 1;
for(int i = 1, x;i <= n; i++) {
x = read(); d[i] = x;
for(int j = 1;j <= x; j++) add(i, read());
}
for(int i = 1;i <= n; i++) if(d[i] <= 2) { s = i; break; } //找出可以作为开头结点的编号最小的点
get_tree(s, 0); d[s] ++; make_ans(s, 0, 1); //s肯定是情况二, 因为它是左儿子或者根
return 0;
}