P4228 [清华集训2017] 榕树之心
给你一棵有根树和一个动点,动点一开始位于根结点 。
一开始这棵树只有根结点,每次可以在已有的结点上沿原树边向外扩展一个结点,并使动点沿着新结点的方向移动一个结点。
请求出每个结点在原树被完全扩展后,动点是否能停留在当前结点。
多测,,时限 ,空限 。
sol
先考虑根节点的答案。
我们可以发现,假设动点当前在点 ,那么我们将 的两个不同子树分别做一次扩展,动点不动,称之为抵消。
根据套路,如果有一个根节点的子树的大小大于总大小(除去根节点)的一半,那么当这个子树被完全扩展后,根节点的其他子树无论如何也不能将它拉回根节点。
否则,即根节点的最大子树小于等于总大小(除去根节点)的一半,那么肯定有一种抵消方案使得最后除根节点外剩下 个节点。
现在考虑如何让根节点的最大子树变小,越小越好。
我们发现,子树内是可以自行抵消的,即将动点拉倒子树内的一个点 ,将 的两个不同子树分别做一次扩展,动点仍然不动。
设 表示 的子树内自行抵消后剩下的最小的节点数。
分类讨论,设 的最大子树的根为 :
- 若 ,则 的大小小于等于总大小(除去 ,只考虑 这个子树)的一半,则 。
- 否则,让其他子树全部去消 ,最后剩下的是 的一部分,则 。
注意,上面情况都没讨论 。
为了让动点可以到达 , 需要由 的父亲向 扩展一遍,所以这个贡献需要加上,最后令 。
对于根节点的答案,设其最大子树为 ,需满足两个条件:
- ,即其最大子树自行消除后剩余的节点根节点的其他子树可被消完。
- ,即其它子树互相消除后没有剩余节点。
再考虑其他节点(设其为 )的答案。
考虑一开始先将动点移动到 ,则我们可以将 的这条链缩成一个点。
如下图:
由上图应该可以看到,如果我们将 的这条链缩成一个点的话,还需保存每个点的次大子树,然后像做根节点的时候一样做即可。
总时间复杂度为 ,可过。
。
#include <bits/stdc++.h>
#define re register
using namespace std;
namespace Fread
{
const int SIZE = 1 << 23;
char buf[SIZE], *S, *T;
inline char getchar()
{
if (S == T)
{
T = (S = buf) + fread(buf, 1, SIZE, stdin);
if (S == T)
return '\n';
}
return *S++;
}
}
namespace Fwrite
{
const int SIZE = 1 << 23;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush()
{
fwrite(buf, 1, S - buf, stdout);
S = buf;
}
inline void putchar(char c)
{
*S++ = c;
if (S == T)
flush();
}
struct NTR
{
~NTR()
{
flush();
}
} ztr;
}
#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif
inline int read()
{
re int x = 0, f = 1;
re char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
x = x * 10 + c - '0', c = getchar();
return x * f;
}
inline void write(int x)
{
if(x < 0)
{
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
const int _ = 100007;
int n;
bool ans[_];
int cnt, head[_], to[_ << 1], nxt[_ << 1];
inline void add(int u, int v)
{
to[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
}
int dep[_], siz[_], hson[_], hson2[_], f[_];
inline int cmp(int x, int y)
{
return siz[x] > siz[y] ? x : y;
}
inline void dfs1(int u, int fa)
{
dep[u] = dep[fa] + 1, siz[u] = 1;
for (re int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if (v == fa)
continue;
dfs1(v, u), siz[u] += siz[v];
if (siz[v] > siz[hson[u]])
hson2[u] = hson[u], hson[u] = v;
else if (siz[v] > siz[hson2[u]])
hson2[u] = v;
}
if (f[hson[u]] <= siz[u] - 1 - siz[hson[u]])
f[u] = (siz[u] - 1) % 2;
else
f[u] = f[hson[u]] - (siz[u] - 1 - siz[hson[u]]);
f[u]++;
}
inline void dfs2(int u, int fa, int h)
{
int tmp = cmp(hson[u], h);
if (f[tmp] <= n - dep[u] - siz[tmp])
ans[u] = (n & 1) == (dep[u] & 1);
for (re int i = head[u]; i; i = nxt[i])
{
re int v = to[i];
if (v == fa)
continue;
dfs2(v, u, v == hson[u] ? cmp(hson2[u], h) : cmp(hson[u], h));
}
}
inline void init()
{
cnt = 0;
memset(head, 0, sizeof head);
memset(hson, 0, sizeof hson);
memset(hson2, 0, sizeof hson2);
memset(ans, 0, sizeof ans);
}
signed main()
{
re int W = read(), T = read();
while (T--)
{
init();
n = read();
for (re int i = 1, u, v; i < n; ++i)
{
u = read(), v = read();
add(u, v), add(v, u);
}
dfs1(1, 0);
dfs2(1, 0, 0);
if (W == 3)
write(ans[1]);
else
for (re int i = 1; i <= n; ++i)
write(ans[i]);
putchar('\n');
}
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122033