1. 题面梗概
中译中: 给你一棵树,让你删掉一条边再加回一条边,使得这棵树变成“单重心”的。
2. 逻辑推导
理论上,一棵树只有两种可能,单重心和双重心。
如果只有一个重心,那么随便搞,走个形式即可。如果有两个重心呢?
2.1 重心
重心的定义是,该点的最大子树大小 ≤N/2。如果是双重心,那就是两个点的最大子树都是 N/2。
如此,我们很容易就能想到,只需要把某一个重心的叶子节点连接到另一个重心,就可以转移重心了。
2.2 结论
这是一个重心性质的简单应用。
3. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| #include <bits/stdc++.h> using namespace std; using ll = long long;
const int maxn = 1e5 + 5;
vector< int > adj[maxn]; int sz[maxn], f[maxn]; int r1, r2; int n;
int leaf, leaf_p;
void find_leaf( int u, int p ) { if( adj[u].size( ) == 1 ) { leaf = u; leaf_p = p; return; } for( int v : adj[u] ) { if( v == p ) continue; find_leaf( v, u ); if( leaf != -1 ) return; } }
void get_root( int u, int p ) { sz[u] = 1; f[u] = 0;
for( int v : adj[u] ) { if( v == p ) continue; get_root( v, u ); sz[u] += sz[v]; f[u] = max( f[u], sz[v] ); } f[u] = max( f[u], n - sz[u] ); if( f[u] < f[r1] ) r1 = u, r2 = 0; else if( f[u] == f[r1] ) r2 = u; }
void solve( ) { cin >> n;
for( int i = 1; i <= n; ++ i ) adj[i].clear( ); r1 = r2 = 0; f[0] = n + 1;
for( int i = 1; i < n; ++ i ) { int u, v; cin >> u >> v; adj[u].push_back( v ); adj[v].push_back( u ); }
get_root( 1, 0 );
if( r2 == 0 ) { cout << adj[r1][0] << ' ' << r1 << '\n'; cout << adj[r1][0] << ' ' << r1 << '\n'; } else { leaf = -1; find_leaf( r1, r2 );
cout << leaf << ' ' << leaf_p << '\n'; cout << leaf << ' ' << r2 << '\n'; } }
int main( ) { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _t = 1; cin >> _t; while( _t -- ) solve( ); return 0; }
|
4. 复盘