题解「Luogu4228 [清华集训2017] 榕树之心」
转载注明来源:https://www.cnblogs.com/syc233/p/13606704.html
题意
给一棵树和一个动点,最初树只有根结点,动点在根节点。每次在已有的结点处向外扩展出一个结点,并且动点沿着新结点到动点的最短路径移动一步。必须保证在任意时刻,树的点集和边集是原树点集和边集的子集。求最终动点可能在哪个结点上。
题解
\(\text{TestPoint 3}\) 暗示我们先从判断根是否合法入手。
对于根 \(u\) ,我们每次选择 \(u\) 的两个儿子对应的子树中的结点进行扩展,那么动点将会回到原地。
于是乎:
zzy:
这里就涉及到一个经典的模型:有若干个元素被分成了若干个集合, 每次要找两个在不同集合中的元素匹配然后消掉。
于是考虑树形DP:
令 \(f_u\) 表示动点最初在 \(u\) 上,扩展完 \(u\) 的子树后动点离 \(u\) 最近距离,\(v\) 表示 \(u\) 的重儿子,\(size_u\) 表示 \(u\) 的子树大小。则有转移:
- \(size_u-size_v-1\geq f_v+1\) ,则 \(f_u=(size_u-1) \ {\rm{mod}} \ 2\) 。
- \(size_u-size_v-1 < f_v+1\) ,则 \(f_u=f_v+1-(size_u-size_v-1)\) 。
若动点最终不在根,则动点停在的点 \(u\) 到根的路径一定会被动点经过,将 \(1\to u\) 这条路径缩成一个点进行树形DP即可。这个点的儿子的信息我们在对根进行DP时已经算出来了,所以没必要再对缩点后的树进行DP。
时间复杂度:\(O(Tn)\) 。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 100005
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
struct edge
{
int u,v,next;
}e[maxn<<1];
int head[maxn],k;
inline void add(int u,int v)
{
e[k]=(edge){u,v,head[u]};
head[u]=k++;
}
int n;
int siz[maxn],f[maxn],son[maxn],ans[maxn];
inline void dfs(int u,int fa)
{
siz[u]=1;
son[u]=-1;
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
if(!~son[u]||siz[son[u]]<siz[v]) son[u]=v;
}
if(!~son[u]) return void(f[u]=0);
if(siz[u]-siz[son[u]]-1>=f[son[u]]+1) f[u]=(siz[u]-1)&1;
else f[u]=f[son[u]]+1-(siz[u]-siz[son[u]]-1);
}
inline void dfs2(int u,int fa,int pre,int sum)
{
if(u==1) ans[u]=f[u];
else
{
int pos=siz[pre]>=siz[son[u]]?pre:son[u],sz=siz[u]+sum;
if(sz-siz[pos]-1>=f[pos]+1) ans[u]=(sz-1)&1;
else ans[u]=f[pos]+1-(sz-siz[pos]-1);
}
int sson=-1;
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].v;
if(v==son[u]||v==fa) continue;
sson=(!~sson||siz[sson]<siz[v])?v:sson;
}
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
int now=(v==son[u])?sson:son[u],tmp=(siz[now]>siz[pre])?now:pre;
dfs2(v,u,tmp,sum+siz[u]-siz[v]-1);
}
}
int main()
{
// freopen("P4228.in","r",stdin);
int W,T;
read(W),read(T);
while(T--)
{
read(n);
memset(head,-1,sizeof(int)*(n+5));k=0;
for(int i=1,u,v;i<n;++i)
{
read(u),read(v);
add(u,v);add(v,u);
}
dfs(1,0);
if(W==3) {puts(f[1]?"0":"1");continue;}
dfs2(1,0,0,0);
for(int i=1;i<=n;++i)
putchar(ans[i]?'0':'1');
puts("");
}
return 0;
}