zoukankan      html  css  js  c++  java
  • [题解] [清华集训 2017] 榕树之心

    题面

    题解

    我们先考虑根的情况, 看是否能够最后停在根节点上

    我们设两棵子树 (u) , (v) , 那么 (u) 长出一个点, (v) 再长出一个点, 这两个点的影响就抵消了对吧

    那么我们就是看是否能子树内互相抵消最后使榕树之心停在根节点上

    最大的一棵子树肯定是最难消的, 我们考虑用其他的子树去消它, 然后其他的子树内部再消

    我们设这棵树以 (u) 为根, 最大的一棵子树以 (v) 为根, 那么可以分为这么几种情况讨论

    • 其他的子树能够把 (v) 整棵树消完, 也就是说 (sz[v] leq sz[u] - 1 - sz[v]) , 这样就能够用其他子树消完 (v) 这棵树, 并且他们内部只要有偶数个点也可以消完, 证明的话你可以交换一些点消去的顺序, 会发现偶数个数下是能够消完的
    • 其他子树不能把 (v) 整棵树消完, 那么 (v) 内部先互相消, 然后其他的兄弟子树再来救火看能不能消完, 设 (v) 内部最多能够消去 (f[v])
    • 若内部消完之后其他子树可以消完剩下的, 即 (sz[v] - 2 * f[v] leq sz[u] - 1 - sz[v]) , 那么看其他子树剩下的分奇偶性讨论一下就行
    • 若内部消完之后其他子树不能消完剩下的, 即 (sz[v] - 2 * f[v] > sz[u] - 1 - sz[v]) , 那么就会留下 (sz[v] - 2 * f[v] - (sz[u] - 1 - sz[v])) 个点没有消

    那么 (f[v]) 怎么推呢

    根据以上过程我们可以发现, 字母定义如上

    [displaystyle f[u] = egin{cases}lfloor frac{sz[u] - 1}{2} floor , sz[v] leq sz[u] - 1 - sz[v]\lfloor frac{sz[u] - 1}{2} floor , sz[v] - 2 * f[v] leq sz[u]- 1 - sz[v]\f[v] + sz[u] - sz[v] - 1, sz[v] - 2 * f[v] > sz[u]-1-sz[v]end{cases} ]

    这样我们就处理出了根节点的情况

    那么对于任意一个点怎么做呢?

    我们可以看做先伸出了 (1 o x) 这一条链, 此时榕树之心在 (x)

    然后再看这条链上的其他子树互相消看是否能够消光就行了

    (x) 有可能在 (1) 的最大的子树里面, 所以我们要记一下次大子树

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    const int N = 100005; 
    using namespace std;
    
    int W, n, T, head[N], cnt, dep[N], sz[N], mson[N], sson[N], f[N], ans[N]; 
    struct edge { int to, nxt; } e[N << 1]; 
    
    template < typename T >
    inline T read()
    {
    	T x = 0, w = 1; char c = getchar();
    	while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * w; 
    }
    
    inline void adde(int u, int v) { e[++cnt] = (edge) { v, head[u] }, head[u] = cnt; }
    
    void dfs(int u, int fa)
    {
    	dep[u] = dep[fa] + 1, sz[u] = 1;
    	for(int v, i = head[u]; i; i = e[i].nxt)
    	{
    		v = e[i].to; if(v == fa) continue;
    		dfs(v, u), sz[u] += sz[v]; 
    		if(sz[v] > sz[mson[u]])
    			sson[u] = mson[u], mson[u] = v; 
    		else if(sz[v] > sz[sson[u]])
    			sson[u] = v; 
    	}
    	if(2 * sz[mson[u]] <= sz[u] - 1 || 2 * (sz[mson[u]] - f[mson[u]]) <= sz[u] - 1)
    		f[u] = (sz[u] - 1) / 2;
    	else
    		f[u] = f[mson[u]] + sz[u] - sz[mson[u]] - 1; 
    }
    
    void dfs2(int u, int fa, int id)
    {
    	int tmp = sz[id] > sz[mson[u]] ? id : mson[u];
    	if(2 * sz[tmp] <= n - dep[u] || 2 * (sz[tmp] - f[tmp]) <= n - dep[u])
    		ans[u] = (n - dep[u]) & 1 ? 0 : 1;
    	for(int v, i = head[u]; i; i = e[i].nxt)
    	{
    		v = e[i].to; if(v == fa) continue;
    		dfs2(v, u, (v == mson[u] ? (sz[id] > sz[sson[u]] ? id : sson[u]) : tmp)); 
    	}
    }
    
    int main()
    {
    	W = read <int> (), T = read <int> (); 
    	while(T--)
    	{
    		n = read <int> (), cnt = 0; 
    		for(int i = 1; i <= n; i++)
    			head[i] = mson[i] = sson[i] = ans[i] = 0; 
    		for(int u, v, i = 1; i < n; i++)
    		{
    			u = read <int> (), v = read <int> (); 
    			adde(u, v), adde(v, u); 
    		}
    		dfs(1, 0), dfs2(1, 0, 0);
    		if(W == 3) printf("%d
    ", ans[1]);
    		else
    		{
    			for(int i = 1; i <= n; i++)
    				printf("%d", ans[i]); 
    			puts("");
    		}
    	}
    	return 0; 
    }
    
  • 相关阅读:
    快速幂模板
    部分有关素数的题
    POJ 3624 Charm Bracelet (01背包)
    51Nod 1085 背包问题 (01背包)
    POJ 1789 Truck History (Kruskal 最小生成树)
    HDU 1996 汉诺塔VI
    HDU 2511 汉诺塔X
    HDU 2175 汉诺塔IX (递推)
    HDU 2077 汉诺塔IV (递推)
    HDU 2064 汉诺塔III (递推)
  • 原文地址:https://www.cnblogs.com/ztlztl/p/12203583.html
Copyright © 2011-2022 走看看