【LOJ】#2330. 「清華集訓 2017」榕樹之心 -樹形dp
阿新 • • 發佈:2019-01-25
題解
先考慮根的情況()。
根的每個兒子及其構成的子樹之間可以互相抵消。
設表示以為根的子樹最少的不能互相抵消的點數。
那麼考慮根的最大兒子,
若,大兒子可以被抵消掉,這時只需考慮是否為偶數即可。
否則
其他,把當前節點到根節點的這條鏈看做根,順便記一下每個點的第二大兒子存一下這條鏈為根的最大兒子。一遍即可。
程式碼
#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("");
}
}
}