題目
題目鏈接
題解:
/*
這道題要用dp+倍增,也就是倍增dp
首先要了解樹的重心的基本性質
1.重心都是相鄰的
2.重心都是在樹的重邊上(不會證明)
那麼就可以dp了,dp[i][j]表示從i節點開始,向下跳2的j次方條重邊所得到的點,轉移和LCA是一樣的
現在要考慮換根,其實可以在線維護,具體看代碼
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <iostream>
#define ll long long
#define reg register
using namespace std;
const int MAXN = 399995;
int a[MAXN];
int f[MAXN][23] , s1[MAXN] , s2[MAXN] , s3[MAXN] , sz[MAXN];//s1是原樹每個點的重兒子,s2是次重兒子
ll ans;//s3存當前樹(換根後)每個點的重兒子,sz是原樹的每個點包含的節點數,sum是現在的樹每個點包含的節點數
int sum[MAXN];
vector<int>G[MAXN];
int fa[MAXN];
int n;
inline void read( int &x ){
x = 0;char s = getchar();
while( s < '0' || s > '9' ) s = getchar();
while(s >= '0' && s <= '9' ){
x = x *10 + s - '0' , s = getchar();
}
}
inline void write( ll x ){
if( x > 9 )
write( x / 10 );
putchar( ( x % 10) + '0' );
}
inline void dfs( int x , int pa ){//這個就是簡單的預處理,沒什麼難度
sz[x] = 1;
for( reg int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == pa ) continue;
fa[v] = x;
dfs( v , x ) ;
sz[x] += sz[v];
if( sz[v] > sz[s1[x]] )
s2[x] = s1[x] , s1[x] = v;
else if( sz[v] > sz[s2[x]] )
s2[x] = v;
}
f[x][0] = s1[x];
for( int i = 1 ; i <= 20 ; i ++ )
f[x][i] = f[f[x][i-1]][i-1];
}
inline ll suan( int x , int mu ){
ll tot = 0;
if( max( sum[s3[x]] , mu - sum[x] ) <= mu / 2 )
tot += x;
return tot;
}
inline void dfs2( int x , int pa ){
for( reg int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == pa ) continue;
sum[x] = sz[1] - sz[v] , sum[v] = sz[v];fa[x] = fa[v] = 0;//斷掉(x,v)這條邊
int k;
if( v == s1[x] )
s3[x] = s2[x];
else
s3[x] = s1[x];//計算重兒子,注意x的父親也成爲x的子節點,也要包含進去
if( sum[pa] > sum[s3[x]] ) s3[x] = pa;
f[x][0] = s3[x];//因爲前面的點都是已經更新好了的,可以直接進行轉移
for( reg int j = 1 ; j <= 20 ; j ++ )
f[x][j] = f[f[x][j-1]][j-1];
k = x;
for( reg int j = 20 ; j >= 0 ; j -- ){
if( f[k][j] && sum[x] - sum[f[k][j]] <= sum[x] / 2 ) k = f[k][j];
}//如果它滿足重心的一個性質,那麼希望它的sum[]值越小越好
ans += suan( k , sum[x] ) + suan( fa[k] , sum[x] );//計算
k = v;
for( reg int j = 20 ; j >= 0 ; j -- ){
if( f[k][j] && sum[v] - sum[f[k][j]] <= sum[v] / 2 ) k = f[k][j];
}
ans += suan( k , sum[v] ) + suan( fa[k] , sum[v] );
fa[x] = v;
dfs2( v , x );
}
s3[x] = s1[x] , sum[x] = sz[x];
fa[x] = pa , f[x][0] = s1[x];
for( reg int j = 1 ; j <= 20 ; j ++ )
f[x][j] = f[f[x][j-1]][j-1];
}
int main(){
int t;
read( t );
while( t -- ){
read( n );
for( reg int i = 1 ; i <= n ; i ++ ){
s1[i] = s2[i] = s3[i] = sz[i] = fa[i] = 0;
sum[i] = 0;
G[i].clear();
}
ans = 0;
for( reg int i = 1 ; i < n ; i ++ ){
int x , y;read( x );read( y );
G[x].push_back( y );
G[y].push_back( x );
}
dfs( 1 , 0 );
for( int i = 1 ; i <= n ; i ++ )
sum[i] = sz[i] , s3[i] = s1[i];//賦值
dfs2( 1, 0 );//開始換根
write( ans );
printf( "\n" );
}
}