[题解] [清华集训 2017] 榕树之心

Posted ztlztl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[题解] [清华集训 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; 
}

以上是关于[题解] [清华集训 2017] 榕树之心的主要内容,如果未能解决你的问题,请参考以下文章

[清华集训2017]小 Y 和地铁(神奇思路,搜索,剪枝,树状数组)

UOJ#339. 清华集训2017小 Y 和二叉树 贪心

清华集训2014 做题记录

「题解」清华集训 2016 你的生命已如风中残烛

2017.11.26清华集训2017模拟

清华集训2014 sum