P4006 小 Y 和二叉树

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;
}
posted @ 2020-11-27 22:46  C锥  阅读(108)  评论(0)    收藏  举报