[CSP-S2019] 樹的重心
阿新 • • 發佈:2021-10-28
Description
給定一棵樹,對於每一條邊,求出割掉該條邊後,兩棵樹的所有可能的重心,輸出重心編號和。
Solution
一個性質是,如果當前點不是重心,那麼重心只可能是往父親方向走或者重兒子方向走。轉換一下,如果割掉 \(u\to v\) 這條邊,分別建兩棵以 \(u\) 和 \(v\) 為根的樹,那麼找重心只需要走重兒子。所以需要處理的只有一個換根操作。我們可以先預處理一個以 \(1\) 為根的所有節點的倍增陣列。考慮當前刪掉 \(u\to v\),重構樹,那麼實際上被改變的祖父關係只有 \(u\) 的根綴上的點,考慮增量更新,回溯的時候再還原即可。詳見程式碼。
#include<stdio.h> #include<algorithm> using namespace std; const int N=3e5+7; inline int read(){ int x=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')flag=0;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();} return flag? x:-x; } struct E{ int next,to; }e[N<<1]; int head[N],cnt=0; inline void add(int id,int to){ e[++cnt]=(E){head[id],to}; head[id]=cnt; e[++cnt]=(E){head[to],id}; head[to]=cnt; } int son[N][2],f[N][19],fa[N],sz[N],S[N],Son[N]; void dfs(int u){ sz[u]=1,son[u][0]=son[u][1]=0; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==fa[u]) continue; fa[v]=u,dfs(v); sz[u]+=sz[v]; if(sz[v]>sz[son[u][0]]) son[u][1]=son[u][0],son[u][0]=v; else if(sz[v]>sz[son[u][1]]) son[u][1]=v; } f[u][0]=son[u][0]; for(int i=1;i<19;i++) f[u][i]=f[f[u][i-1]][i-1]; } inline int check(int u,int sz){ return max(S[Son[u]],sz-S[u])<=sz/2? /*printf("%d ",u),*/u:0; } long long ans=0; void Dfs(int u,int Fa){ for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(Fa==v) continue; S[u]=sz[1]-sz[v]; fa[u]=fa[v]=0; //split it into Two subtrees,regard u,v as root Son[u]=(son[u][0]!=v? son[u][0]:son[u][1]); if(S[Fa]>S[Son[u]]) Son[u]=Fa; f[u][0]=Son[u]; int rt=u; for(int j=1;j<19;j++) f[u][j]=f[f[u][j-1]][j-1]; for(int j=18;~j;j--) if(f[rt][j]&&S[u]-S[f[rt][j]]<=S[u]/2) rt=f[rt][j]; // printf("[%d,%d] ",u,v); ans+=check(rt,S[u])+check(fa[rt],S[u])+check(Son[rt],S[u]); rt=v; for(int j=18;~j;j--) if(f[rt][j]&&S[v]-S[f[rt][j]]<=S[v]/2) rt=f[rt][j]; ans+=check(rt,S[v])+check(fa[rt],S[v])+check(Son[rt],S[v]);// printf("\n"); fa[u]=v,Dfs(v,u); } Son[u]=f[u][0]=son[u][0],fa[u]=Fa,S[u]=sz[u]; for(int i=1;i<19;i++) f[u][i]=f[f[u][i-1]][i-1]; } int main(){ int T=read(); while(T--){ int n=read(); cnt=0; for(int i=1;i<=n;i++) head[i]=fa[i]=0; for(int i=2;i<=n;i++) add(read(),read()); dfs(1); for(int i=1;i<=n;i++) S[i]=sz[i],Son[i]=son[i][0]; ans=0,Dfs(1,0); printf("%lld\n",ans); } }