1. 程式人生 > >【LOJ】#2330. 「清華集訓 2017」榕樹之心 -樹形dp

【LOJ】#2330. 「清華集訓 2017」榕樹之心 -樹形dp

題解

先考慮根的情況(Subtask3)。
根的每個兒子及其構成的子樹之間可以互相抵消。
rem[i]表示以i為根的子樹最少的不能互相抵消的點數。
那麼考慮根的最大兒子mx[1]
sz[1]1sz[mx[1]]rem[mx[1]],大兒子可以被抵消掉,這時只需考慮sz[1]1是否為偶數即可。
否則rem[1]=rem[mx[1]]sz[1]+sz[mx[1]]+2
其他Su

btask,把當前節點到根節點的這條鏈看做根,順便記一下每個點的第二大兒子mxx[i]res[i]存一下這條鏈為根的最大兒子。dfs一遍即可。

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;

int W,T,n,d[N],sz[N],ans[N];
int rem[N],mx[N],mxx[N],f[N],res[N];
int head[N],to[N<<1],nxt[N<<1],tot;

inline int rd()
{
    char
ch=getchar();int x=0,f=1; while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();} return x*f; } inline void lk(int u,int v) {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;} inline void dfs(int x) { int i,j; d[x]=d[f[x]]+1;sz[x]=1;mx[x]=mxx[x]=0
; for(i=head[x];i;i=nxt[i]){ j=to[i];if(j==f[x]) continue; f[j]=x;dfs(j);sz[x]+=sz[j]; if(sz[mx[x]]<sz[j]){mxx[x]=mx[x];mx[x]=j;} else if(sz[mxx[x]]<sz[j]) mxx[x]=j; } if(sz[x]-1-sz[mx[x]]>=rem[mx[x]]) rem[x]=((sz[x]-1)&1)+1; else rem[x]=rem[mx[x]]-sz[x]+2+sz[mx[x]]; } inline void df(int x) { int i,j;ans[x]=0; if(!((sz[1]-d[x])&1)){ j=res[x];if(sz[mx[x]]>sz[res[x]]) j=mx[x]; if((sz[1]-d[x]-sz[j])>=rem[j]) ans[x]=1; } for(i=head[x];i;i=nxt[i]){ j=to[i];if(j==f[x]) continue; res[j]=res[x]; if((j!=mx[x])&&(sz[mx[x]]>sz[res[j]])) res[j]=mx[x]; else if(sz[mxx[x]]>sz[res[j]]) res[j]=mxx[x]; df(j); } } int main(){ int i,j,ix,iy; W=rd(),T=rd(); while(T--){ n=rd(); for(tot=0,i=1;i<=n;++i) head[i]=0; for(i=1;i<n;++i){ix=rd();iy=rd();lk(ix,iy);lk(iy,ix);} dfs(1); if(W==3){ if((!((sz[1]-1)&1)) && (sz[1]-1-sz[mx[1]])>=rem[mx[1]]) puts("1"); else puts("0"); }else{ res[1]=0;df(1); for(i=1;i<=n;++i) printf("%c",'0'+ans[i]); puts(""); } } }