【清华集训2017】榕树之心

简要题意:(考虑到某些人搜题解只是懒得看题面)
特殊点从 \(1\) 号点出发,每次选择一个与当前选择连通块相连的点,加入连通块,并且把我们的特殊点向那个选的点移动一格。对每个点求是否能成为我们的特殊点的终点。\(1\) 处在一开始的连通块之中。

不错的一道题。但是如果要很快地写对需要冷静思考(像我就不知道在干什么)。像我这样的菜鸡看了题解才会……

先考虑简单的部分,即 \(W = 3\),只输出 \(1\) 的答案。

先根据特殊点的深度的奇偶性来判断一个点有没有可能(即 \(dep_x + n\) 的奇偶性)。

根据套路,如果有一个子树的大小大于总大小的一半,那么那个子树显然能贡献很多,但是不一定会无解。如果不存在,显然能构造出一个方案(即分成三部分,一个部分里只有一个,另外两个部分大小分别都小于一半,显然有解)。

所以现在只要考虑那个最大的子树,如果我们把它消的小了,那么还是有可能的。记 \(f_i\) 为向 \(i\) 走能造成的最小的相对深度(不是相对 \(i\) 而是相对走到 \(i\) 的那个点,即 \(i\) 也会对这个深度产生贡献。这样做能方便各种地方的细节)。

显然,考虑如果 \(u\) 向 最大的子树 \(v\) 走,此时如果 \(sz_u - sz_v - 1\) 即其他子树的大小是比 \(f_v\) 大的,又因为 \(sz_v > sz_u - sz_v - 1\),那么显然能构造出一种方案,使得 \(v\) 的贡献与 \(sz_u - sz_v - 1\) 相差不超过 \(1\) 。所以直接根据奇偶性进行转移,即 \(f_u = sz_u - 1 \mod 2\)

但是如果这个比不上 \(f_v\) ,那么其他的子树肯定是要拿去消 \(f_v\) 的,所以 \(f_u = f_v - (sz_u - sz_v - 1) + 1\)

所以现在已经做完这个简单的部分了。考虑其他点的情况。因为路径上的点是必经的,所以如果把这条路径缩成一个点,上述的过程依然能进行。(因为如果点可行,那就是对消的,那么在链上显然可以)

所以我们仍维护那个最大的子树。因为一条链都缩成一个点了,那么这些子树都是原树某个点的子树。因此不必要考虑换根DP的各种细节,直接类似上面的过程处理下去即可。

注意数据的清空。同时因为变量初始化的原因,以及转移没推清楚,先RE了一发,又WA成45了一发。

#include <bits/stdc++.h>

const int MAXN = 100010;

int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot;
void addedge(int b, int e) {
	nxt[++tot] = head[b]; to[head[b] = tot] = e;
	nxt[++tot] = head[e]; to[head[e] = tot] = b;
}
int sz[MAXN], fir[MAXN], sec[MAXN];
int f[MAXN], ansl[MAXN], n;
int cmp(int a, int b) { // a >= b
	return sz[a] == sz[b] ? f[a] <= f[b] : sz[a] > sz[b];
}
void gx(int & fir, int & sec, int v) {
	if (cmp(v, fir)) sec = fir, fir = v;
	else if (cmp(v, sec)) sec = v;
}
void dfs1(int u, int fa = 0) {
	sz[u] = 1;
	for (int i = head[u]; i; i = nxt[i])
		if (to[i] != fa) {
			dfs1(to[i], u);
			sz[u] += sz[to[i]];
			gx(fir[u], sec[u], to[i]);
		}
	if (!fir[u]) return ;
	int R = sz[u] - 1 - sz[fir[u]];
	if (R > f[fir[u]]) f[u] = sz[u] + 1 & 1; else f[u] = f[fir[u]] + 1 - R;
	// std::cout << u << ' ' << sz[u] << " : " << fir[u] << ' ' << sec[u] << " : " << f[u] << ' ' << R << std::endl;
}
void dfs2(int u, int fa = 0, int ma = 0, int dep = 0) {
	int t, v;
	gx(v = ma, t = 0, fir[u]);
	if (dep + n & 1)
		ansl[u] = n - dep - 1 - sz[v] >= f[v];
	for (int i = head[u]; i; i = nxt[i]) if (to[i] != fa) {
		gx(v = ma, t = 0, to[i] == fir[u] ? sec[u] : fir[u]);
		dfs2(to[i], u, v, dep + 1);
	}
}
int main() {
	// freopen("1.in", "r", stdin);
	std::ios_base::sync_with_stdio(false), std::cin.tie(0);
	int W, T; std::cin >> W >> T;
	while (T --> 0) {
		std::cin >> n;
		for (int i = 1, t1, t2; i < n; ++i) {
			std::cin >> t1 >> t2;
			addedge(t1, t2);
		}
		dfs1(1); dfs2(1);
		for (int i = 1; i <= (W == 3 ? 1 : n); ++i)
			std::cout << ansl[i];
		std::cout << '\n';
		memset(head, 0, n + 1 << 2); tot = 0;
		memset(ansl, 0, n + 1 << 2);
		memset(fir, 0, n + 1 << 2);
		memset(sec, 0, n + 1 << 2);
		memset(f, 0, n + 1 << 2);
	}
	return 0;
}
posted @ 2019-09-23 21:18  daklqw  阅读(259)  评论(0编辑  收藏  举报