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; }