P5666 题解
第二篇题解的思路很好,但是过于简略了。
树的重心有一个性质:对于以
然后就类似 LCA,我们可以设
关于实现,可以通过换根的方式,维护换根后每个结点的子树大小、父亲、重儿子。
时间复杂度
#include <bits/stdc++.h>
using namespace std;
#define MAXN 300000
#define MAXK 19
#define judge(x, sum) (x) * (max(siz2[son3[x]], (sum)-siz2[x]) <= (sum >> 1)) // 判断是否为重心
int t, n, u, v;
int fa[MAXN], fa2[MAXN]; // 父亲
int siz[MAXN], siz2[MAXN]; // 子树大小
int son[MAXN], son2[MAXN], son3[MAXN]; // 重儿子、次重儿子
int p[MAXN][MAXK];
list<int> e[MAXN];
long long ans;
void clear(){
ans = 0;
memset(son, 0, sizeof(son));
memset(p, 0, sizeof(p));
for (int i(1); i<=n; ++i) e[i].clear();
}
void dfs1(int x, int f){
siz[x] = 1;
fa[x] = f;
for (auto i: e[x]){
if (i ^ f){
dfs1(i, x);
siz[x] += siz[i];
if (siz[son[x]] < siz[i]){
son2[x] = son[x];
son[x] = i;
}else if (siz[son2[x]] < siz[i]) son2[x] = i;
}
}
p[x][0] = son[x];
for (int i(1); i<MAXK; ++i) p[x][i] = p[p[x][i-1]][i-1];
}
void dfs2(int x){
for (auto i: e[x]){
if (i ^ fa[x]){
siz2[x] = n - siz[i];
fa2[x] = fa2[i] = 0;
son3[x] = (son[x] == i ? son2[x] : son[x]);
if (siz2[fa[x]] > siz2[son3[x]]) son3[x] = fa[x];
p[x][0] = son3[x];
for (int j(1); j<MAXK; ++j) p[x][j] = p[p[x][j-1]][j-1];
int tmp(x); // 求出 x 的重心
for (int j(MAXK-1); j>=0; --j){
if (siz2[p[tmp][j]] >= ((siz2[x]+1) >> 1)) tmp = p[tmp][j];
}
ans += judge(tmp, siz2[x]) + judge(fa2[tmp], siz2[x]);
tmp = i; // 求出 i 的重心
for (int j(MAXK-1); j>=0; --j){
if (siz2[p[tmp][j]] >= ((siz2[i]+1) >> 1)) tmp = p[tmp][j];
}
ans += judge(tmp, siz2[i]) + judge(fa2[tmp], siz2[i]);
fa2[x] = i;
dfs2(i);
}
}
son3[x] = p[x][0] = son[x];
for (int i(1); i<MAXK; ++i) p[x][i] = p[p[x][i-1]][i-1];
fa2[x] = fa[x];
siz2[x] = siz[x];
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--){
cin >> n;
clear();
for (int i(1); i<n; ++i){
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1, 0);
memcpy(siz2, siz, sizeof(siz));
memcpy(son3, son, sizeof(son));
memcpy(fa2, fa, sizeof(fa2));
dfs2(1);
cout << ans << '\n';
}
return 0;
}