洛谷 P5666 树的重心
洛谷 P5666 树的重心
https://www.luogu.com.cn/problem/P5666
Tutorial
https://www.luogu.com.cn/blog/soaring/solution-p5666
关于树的重心有一条性质,假如\(u\)不是重心,那么重心一定在\(u\)的size最大的子树中.
利用这个性质我们可以利用倍增的思想快速找到重心.即维护\(jump(u,i)\)表示从\(u\)出发,向重儿子走\(2^i\)步所到达的节点.
由于要对于每条边统计,所以用换根dp的思想,在dfs的时候维护以当前节点为根时的\(jump\)等信息即可.
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
inline char nc() {
return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T &x) {
x=0; int f=1,ch=nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
x*=f;
}
typedef long long ll;
const int maxn=299995+50;
int T,n;
int head[maxn];
int anc[maxn],siz[maxn],son[maxn][2];
int pre[maxn],s[maxn],jump[maxn][20];
ll an;
struct edge {
int to,nex;
edge(int to=0,int nex=0):to(to),nex(nex){}
};
vector<edge> G;
inline void addedge(int u,int v) {
G.push_back(edge(v,head[u])),head[u]=G.size()-1;
G.push_back(edge(u,head[v])),head[v]=G.size()-1;
}
void dfs0(int u) {
siz[u]=1,son[u][0]=son[u][1]=0;
for(int i=head[u];~i;i=G[i].nex) {
int v=G[i].to; if(v==anc[u]) continue;
anc[v]=u;
dfs0(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u][0]]) son[u][1]=son[u][0],son[u][0]=v;
else if(siz[v]>siz[son[u][1]]) son[u][1]=v;
}
pre[u]=anc[u],s[u]=siz[u],jump[u][0]=son[u][0];
for(int i=1;i<20;++i) jump[u][i]=jump[jump[u][i-1]][i-1];
}
inline int check(int x,int r) {
if(s[jump[x][0]]>s[r]/2) return 0;
return x;
}
void sol(int u) {
int x=u;
for(int i=19;~i;--i) if(s[u]-s[jump[x][i]]<=s[u]/2) x=jump[x][i];
an+=check(x,u)+check(pre[x],u);
}
void dfs1(int u) {
for(int i=head[u];~i;i=G[i].nex) {
int v=G[i].to; if(v==anc[u]) continue;
pre[u]=pre[v]=0,s[u]=n-siz[v];
jump[u][0]=son[u][v==son[u][0]];
if(n-siz[u]>siz[jump[u][0]]) jump[u][0]=anc[u];
for(int j=1;j<20;++j) jump[u][j]=jump[jump[u][j-1]][j-1];
sol(u),sol(v);
pre[u]=v;
dfs1(v);
}
pre[u]=anc[u],s[u]=siz[u],jump[u][0]=son[u][0];
for(int i=1;i<20;++i) jump[u][i]=jump[jump[u][i-1]][i-1];
}
int main() {
read(T);
for(int kase=1;kase<=T;++kase) {
read(n);
memset(head,-1,sizeof(head)),G.clear();
for(int i=1;i<n;++i) {
int u,v; read(u),read(v);
addedge(u,v);
}
an=0;
dfs0(1);
dfs1(1);
printf("%lld\n",an);
}
return 0;
}