1. 程式人生 > 實用技巧 >CSP2019樹的重心

CSP2019樹的重心

題解:

CSP2019D2T3

首先我們要明確一個性質,那就是對於一棵樹的任何一個節點來說,如果這個點不是重心,那麼這棵樹的重心就一定在這個節點的以重兒子為根節點的子樹裡

證明顯而易見,因為該點不是重心所以siz[重兒子]一定大於$\lfloor \frac{siz[x]}{2} \rfloor$

另外還有一個重心的定義:重心所有子樹的大小一定小於等於$\lfloor \frac{siz[重心]}{2} \rfloor$

我們首先遍歷整棵樹,切斷一條邊(u,v)後,我們得到以u為根、以v為根的兩顆樹

然後不斷向重兒子跳,直到找到重心

這裡我們可以用倍增實現,時間複雜度$O(N log_{2} N)$

我們預處理倍增陣列fv[x][i],然後對於以v為根的子樹,直接倍增找重兒子即可

對於以u為根的子樹,也可以用類似的方法解決,我們只需要讓u的父親變成u的兒子,即f[fa]=u,hv[u][0]=siz[hv[u][0]]<siz[fa]-siz[u]?fa:hv[u][0],然後就好了

其實這個題還蠻難的

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define
rep(i,a,b) for(int i=a;i<=b;i++) #define dwn(i,a,b) for(int i=a;i>=b;i--) #define MAXN 300004 using namespace std; typedef long long ll; ll ans; int T,n,tot,nxt[MAXN<<1],to[MAXN<<1],fir[MAXN],hv[MAXN][20],f[MAXN],siz[MAXN],shv[MAXN],thv[MAXN],tsiz[MAXN],S; int read(){ int x=0,f=1;
char ch=getchar(); while('0'>ch || ch>'9'){if(ch=='-') f=-1; ch=getchar();} while('0'<=ch && ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();} return x*f; } void ade(int x,int y){ to[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot; } void dfs1(int x,int fa){ f[x]=fa; hv[x][0]=shv[x]=thv[x]=0; tsiz[x]=1; for(int k=fir[x];k;k=nxt[k]){ if(to[k]==fa) continue; dfs1(to[k],x); tsiz[x]+=tsiz[to[k]]; if(tsiz[to[k]]>tsiz[thv[x]]) shv[x]=thv[x],thv[x]=to[k]; else if(tsiz[to[k]]>tsiz[shv[x]]) shv[x]=to[k]; } hv[x][0]=thv[x]; siz[x]=tsiz[x]; } void init(){ rep(i,1,19) rep(j,1,n) hv[j][i]=hv[hv[j][i-1]][i-1]; } bool chk(int x,int y){ if(!x) return 0; return ((max(siz[hv[x][0]],y-siz[x])*2)<=y); } void recalc(int x){ rep(i,1,19) hv[x][i]=hv[hv[x][i-1]][i-1]; } void clear(){ memset(fir,0,sizeof(fir)); tot=ans=0; } void dfs2(int x,int y){ int now; for(int k=fir[x];k;k=nxt[k]){ if(to[k]==y) continue; if(to[k]==thv[x]) hv[x][0]=shv[x]; else hv[x][0]=thv[x]; if(siz[y]>siz[hv[x][0]]) hv[x][0]=y; recalc(x); siz[to[k]]=tsiz[to[k]]; siz[x]=S-tsiz[to[k]]; now=x; dwn(i,19,0) if(hv[now][i] && siz[x]-siz[hv[now][i]]<=(siz[x]/2)) now=hv[now][i]; // cout<<x<<" "<<now<<"FDF"<<" "<<f[now]<<" "<<f[now]*chk(f[now],siz[x])<<endl; ans=ans+now*chk(now,siz[x])+f[now]*chk(f[now],siz[x]); now=to[k]; dwn(i,19,0) if(hv[now][i] && siz[to[k]]-siz[hv[now][i]]<=(siz[to[k]]/2)) now=hv[now][i]; ans=ans+now*chk(now,siz[to[k]])+f[now]*chk(f[now],siz[to[k]]); f[x]=to[k]; dfs2(to[k],x); } siz[x]=tsiz[x]; hv[x][0]=thv[x]; f[x]=y; recalc(x); } int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); clear(); rep(i,1,n-1){ int x=read(),y=read(); ade(x,y); ade(y,x); } dfs1(1,0); init(); S=tsiz[1]; dfs2(1,0); printf("%lld\n",ans); } return 0; }