题解 P5666 【树的重心】
[CSP2019] 树的重心
题意
所以
具体做法:
设
首先, 我们随便找一个点作为根节点, 那么这里我们默认选择
总时间复杂度
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=299995+7;
const int L=20;
int T,n,sz[N],son[N][2],s[N][L+7];
ll ans;
int lst[N],nxt[2*N],to[2*N],tot;
void add(int x,int y){ nxt[++tot]=lst[x]; to[tot]=y; lst[x]=tot; }
void rfs(int u){
s[u][0]=son[u][0];
for(int i=1;i<=L;i++)
s[u][i]=s[s[u][i-1]][i-1];
}
void pre(int u,int fa){
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
pre(v,u);
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;
}
sz[u]++;
rfs(u);
}
void find(int u){
int t=u;
for(int i=L;i>=0;i--)
if(sz[s[u][i]]>sz[t]/2)
u=s[u][i];
if(sz[t]-sz[u]<=sz[t]/2) ans+=(ll)u;
u=s[u][0];
if(sz[t]-sz[u]<=sz[t]/2) ans+=(ll)u;
}
void run(int u,int fa){
int t1=son[u][0],t2=sz[u]; // 先备份
for(int i=lst[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
if(v==t1) son[u][0]= sz[son[u][1]]>n-t2 ?son[u][1] :fa; // 重新选取 u 的重儿子.
else son[u][0]= sz[t1]>n-t2 ?t1 :fa;
sz[u]=n-sz[v];
rfs(u); // 记得把 son[u][i] 也更新一遍
find(u); find(v);
run(v,u);
}
son[u][0]=t1; // 还原
sz[u]=t2;
rfs(u); //更新
}
int main(){
// freopen("gc.in","r",stdin);
cin>>T;
while(T--){
scanf("%d",&n);
memset(lst,0,sizeof(lst));
memset(son,0,sizeof(son));
memset(sz,0,sizeof(sz));
tot=ans=0;
int x,y;
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
pre(1,0);
run(1,0);
printf("%lld\n",ans);
}
return 0;
}