「清华集训2017」榕树之心

「清华集训2017」榕树之心


“已经快是严冬了,榕树的叶子还没落呢……”

“榕树是常绿树,是看不到明显的落叶季节的……”

“唉……想不到已经七年了呢。榕树还是当年的榕树,你却不是当年的你了……”

“其实又有什么是一成不变的呢,榕树常绿,翠绿树冠的宏观永恒,是由无数细小树叶的荣枯更迭组成的。在时间的流逝中一切都在不断变化着呢……”

“但你看这榕树,日日如此,季季如此,年年如此,仿佛亘古不变般,盘根错节,郁郁葱葱。我在想,或许成为一棵树更好吧,任时间从枝叶间流过,我只守这一片绿荫就好。”

“榕树固然长久,但在这无限的时光里,终归是要湮灭于尘土的。与其像榕树一般,植根于一方泥土中感受年复一年的四季更替。倒不如在有限的时间里看过尽可能多的世界吧。再说了,榕树虽生长缓慢,却依旧会在每年春天抽出一根新的枝条去向外探索的呢……”

“真的吗,榕树在她漫长的一生里,就是这样往外一步步探索的吗?”

“毕竟就算树冠看起来一成不变,榕树也会随着时间周期变化,春天到了自然就是生长的时候了,她也应当做出对应的表现吧……”

“相比于对季节更替做出本能的生长,我倒宁愿相信,榕树有一颗活跃的的,探索的心。”

“其实榕树是有心的,榕树刚刚种下的时候,心就在根的地方发芽了。以后每年春天榕树长出新枝条的时候,心就会向着新枝条的方向移动一点,这样就能更靠近外面的世界了。你看这头顶上的枝条,纵横交错,其实心已经在这枝杈间,移动了数十载了呢……”

“哇,也就是说,这密密麻麻的树杈中的某个地方,藏着这棵榕树的心吗?”

“没错,可是要知道它在哪,就得另花一番功夫了……”

“呀,这时候想想,一株树还是不如一个人好……比如你,要是这样贴上去的话,就能听到跳动的声音呢……”


解题思路

先判断最后能否到 \(1\) 怎么做,考虑每一个节点不同儿子子树的两次操作会相互抵消,显然移回当前节点剩下最少操作的方案一定是剩下若干来自最大儿子子树的点,那么只需要尽可能消除最大儿子子树即可。

\(tot[u]\) 表示从 \(u\) 子树内从 \(u\) 出发最后回到 \(u\) ,最少剩余的生长操作数量。

\[\begin{cases} tot[u] = sz[u] \bmod 2, & sz[u]-sz[v]-1 \geq tot[v]+1 \\ tot[u]=tot[v]+1-(sz[u]-tot[v]-1), &otherwise \end{cases} \]

其中 \(v\)\(u\) 的所有儿子中子树大小最大的那个,考虑如果最大儿子内部消完剩下的东西可以直接用其它儿子的子树消掉,那么答案就是 \(sz[u] \bmod 2\) ,否则每一个其它儿子子树内的点都能和最大儿子剩下的东西消一下,直接减即可。

考虑一次性要求所有点的可行性怎么做,考虑在移到 \(u\) 时当前已经完成的操作一定是 \(1-u\) 的路径以及路径上挂着的一些子树,事实上一定存在一种策略可以让这些挂着的子树互相消除,那么问题就转化成和判断 \(1\) 的可行性类似的问题,把 \(1-x\) 路压缩起来当做新根即可。

code

/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 1000005;
vector<int> g[N];
int sz[N], ms[N], tot[N], ans[N], n;
inline void dfs(int u, int fa){
	sz[u] = 1;
	for(int i = 0; i < (int) g[u].size(); i++){
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u), sz[u] += sz[v];
		if(sz[v] > sz[ms[u]]) ms[u] = v;
	}
	if(!ms[u]) return; 
	if(tot[ms[u]] + 1 <= sz[u] - sz[ms[u]] - 1)
		tot[u] = ((sz[u] - 1) & 1);
	else tot[u] = tot[ms[u]] + 1 - (sz[u] - sz[ms[u]] - 1);
}
inline void dfs2(int u, int fa, int anc, int add){
	int ms2 = 0;
	if(u > 1){
		int size = sz[u] + add, mx;
		if(sz[ms[u]] > sz[anc]) mx = ms[u]; else mx = anc;
		if(tot[mx] + 1 <= size - sz[mx] - 1) ans[u] = ((size - 1) & 1);
		else ans[u] = tot[mx] + 1 - (size - sz[mx] - 1);
	}
	for(int i = 0; i < (int) g[u].size(); i++)
		if(g[u][i] != fa && g[u][i] != ms[u] && sz[g[u][i]] > sz[ms2]) ms2 = g[u][i];
	for(int i = 0; i < (int) g[u].size(); i++){
		int v = g[u][i], tmp = 0;
		if(v == fa) continue;
		if(v == ms[u]) tmp = sz[ms2] > sz[anc] ? ms2 : anc;
		else tmp = sz[ms[u]] > sz[anc] ? ms[u] : anc;
		dfs2(v, u, tmp, add + sz[u] - sz[v] - 1);
	}
}
int main(){
	int T, type; read(type), read(T); while(T--){
		read(n);
		for(int i = 1; i <= n; i++) g[i].clear(), sz[i] = tot[i] = ms[i] = ans[i] = 0;
		for(int i = 1, x, y; i < n; i++){
			read(x), read(y);
			g[x].push_back(y), g[y].push_back(x);
		}
		dfs(1, 0);
		ans[1] = tot[1];
		dfs2(1, 0, 0, 0);
		if(type == 3){ printf("%d\n", !ans[1]); continue; }
		for(int i = 1; i <= n; i++) putchar(!ans[i] ?'1':'0');
		puts("");
	}
	return 0;
}	
posted @ 2019-03-10 16:44  Joyemang33  阅读(424)  评论(0编辑  收藏  举报