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

    转载注明来源:https://www.cnblogs.com/syc233/p/13606704.html


    题意

    给一棵树和一个动点,最初树只有根结点,动点在根节点。每次在已有的结点处向外扩展出一个结点,并且动点沿着新结点到动点的最短路径移动一步。必须保证在任意时刻,树的点集和边集是原树点集和边集的子集。求最终动点可能在哪个结点上。


    题解

    ( ext{TestPoint 3}) 暗示我们先从判断根是否合法入手。

    对于根 (u) ,我们每次选择 (u) 的两个儿子对应的子树中的结点进行扩展,那么动点将会回到原地。

    于是乎:

    zzy:

    这里就涉及到一个经典的模型:有若干个元素被分成了若干个集合, 每次要找两个在不同集合中的元素匹配然后消掉。

    于是考虑树形DP:

    (f_u) 表示动点最初在 (u) 上,扩展完 (u) 的子树后动点离 (u) 最近距离,(v) 表示 (u) 的重儿子,(size_u) 表示 (u) 的子树大小。则有转移:

    • (size_u-size_v-1geq f_v+1) ,则 (f_u=(size_u-1) { m{mod}} 2)
    • (size_u-size_v-1 < f_v+1) ,则 (f_u=f_v+1-(size_u-size_v-1))

    若动点最终不在根,则动点停在的点 (u) 到根的路径一定会被动点经过,将 (1 o u) 这条路径缩成一个点进行树形DP即可。这个点的儿子的信息我们在对根进行DP时已经算出来了,所以没必要再对缩点后的树进行DP。

    时间复杂度:(O(Tn))


    ( ext{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;
    }
    
  • 相关阅读:
    WPF 对显示列顺序的缓存处理
    WPF DataGrid Header语言切换处理
    ini操作类
    C# 校验 正则
    TreeView 数据绑定及选中命令处理
    Sql server Compact 小型数据库损坏修复
    单例模式 与lock用法
    C#串口控制
    mysql group by 组内排序
    mysql general log开启
  • 原文地址:https://www.cnblogs.com/syc233/p/13606704.html
Copyright © 2011-2022 走看看