UOJ#345. 【清华集训2017】榕树之心 贪心,动态规划
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ345.html
前言
我真的是越来越菜了,连树形DP都感觉陌生了。
题解
首先,我们来看看在不断生长叶子会发生什么。
第一种:顺着生长方向走。
第二种:在某一个节点的某些子树依次生长,达到他们之间互相消耗的作用。
对于一个子树 x,假设初始情况下,树心在点 x 上,那么在 x 子树中生长若干次之后,树心离开 x,那么我们考虑求出在树心离开 x 之后,能将 x 往 x 子树方向拉的次数的上界 Max[x] 和下界 Min[x] 。
设 size[x] 表示 x 子树的大小。
显然 Max[x] = size[x]-1 。
而 Min[x] 怎么求?
如果 x 是叶子,那么显然 Min[x] = 0; 如果 x 不是,那么设 x 的儿子中 Max 值最大的节点为 y ,设 $s = \sum_{i\neq y} (Max[i]+1)$ 如果 $s \geq Min[y]+1$ ,则 Min[x] = (s-(Min[y]+1)) mod 2 ,否则 Min[x] = Min[y]+1 - s 。
于是我们已经可以得到根节点的答案了。
那么其他节点呢?
其实大同小异,类似于换根dp地从根出发 dp 一边就好了。
代码
#include <bits/stdc++.h> #define clr(x) memset(x,0,sizeof (x)) #define For(i,a,b) for (int i=a;i<=b;i++) #define Fod(i,b,a) for (int i=b;i>=a;i--) #define pb(x) push_back(x) #define mp(x,y) make_pair(x,y) #define fi first #define se second #define real __zzd001 #define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I') #define outval(x) printf(#x" = %d\n",x) #define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("") #define outtag(x) puts("----------"#x"----------") #define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);\ For(_v2,L,R)printf("%d ",a[_v2]);puts(""); using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef vector <int> vi; LL read(){ LL x=0,f=0; char ch=getchar(); while (!isdigit(ch)) f|=ch=='-',ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } const int N=100005; int W,T,n; vector <int> e[N]; int size[N],Min[N]; int fa[N]; void delfa(int x,int pre){ size[x]=1,fa[x]=pre; for (auto y : e[x]) if (y!=pre) delfa(y,x),size[x]+=size[y]; for (int i=0;i<(int)e[x].size();i++) if (e[x][i]==pre){ swap(e[x][i],e[x].back()); e[x].pop_back(); break; } } void dfs(int x){ for (auto y : e[x]) dfs(y); if (e[x].size()>0){ int s=size[x]-1,mx=0,mi=-1; for (auto y : e[x]) if (mx<=size[y]) mx=size[y],mi=Min[y]+1; s-=mx; if (s>=mi) Min[x]=(s-mi)&1; else Min[x]=mi-s; } else Min[x]=0; } int res[N]; void solve(int x,int d,int s=0){ int fsize=n-size[x]-d; if (max(Min[x],s)<=min(size[x]-1,fsize)) res[x]=1; else res[x]=0; if (e[x].empty()) return; int len=e[x].size()+1; vector <int> sum(len,0),mi(len,-1),mx(len,0); sum[0]=fsize,mx[0]=fsize,mi[0]=s; for (int i=1;i<len;i++){ int y=e[x][i-1]; sum[i]=sum[i-1]+size[y]; if (mx[i-1]<=size[y]) mx[i]=size[y],mi[i]=Min[y]+1; else mx[i]=mx[i-1],mi[i]=mi[i-1]; } int sumR=0,miR=-1,mxR=0; for (int i=len-1;i>=1;i--){ int y=e[x][i-1]; int Sum=sum[i-1]+sumR; int Mx=mxR,Mi=miR; if (Mx<=mx[i-1]) Mx=mx[i-1],Mi=mi[i-1]; Sum-=Mx; solve(y,d+1,Sum>=Mi?((Sum-Mi)&1):Mi-Sum); sumR+=size[y]; if (mxR<=size[y]) mxR=size[y],miR=Min[y]+1; } } void chk(int x,int v){ res[x]&=v; for (auto y : e[x]) chk(y,v^1); } void Solve(){ n=read(); For(i,0,n) e[i].clear(); For(i,1,n-1){ int x=read(),y=read(); e[x].pb(y),e[y].pb(x); } delfa(1,0); dfs(1); solve(1,0); chk(1,n&1); if (W==3) printf("%d\n",res[1]); else { for (int i=1;i<=n;i++) putchar('0'+res[i]); puts(""); } } int main(){ W=read(),T=read(); while (T--) Solve(); return 0; }