<span>题解「Luogu4228 [清华集训2017] 榕树之心」</span>

转载注明来源: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;
}
全部评论

相关推荐

废铁汽车人:秋招真是牛鬼蛇神齐聚一堂
点赞 评论 收藏
分享
joe2333:怀念以前大家拿华为当保底的日子
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务