Codeforces Round #670 (Div. 2)(树的重心,dfs求子树大小)
地址:http://codeforces.com/contest/1406/problem/C
题意:
给出n个点,n-1条边。
通过删除一条边,增加一条边,使得重心唯一
重心:树的某个节点,当去掉该节点后,树的各个连通分量中,节点数最多的连通分量其节点数达到最小值。
解析:
关于重心的一个性质:
一棵树最多两个重心,若两个,则相邻。
那么,如果一个点x为重心,其子树点的数目一定为n/2,那么与他相邻的那个点y,其含有的点数目要么为n/2,要么为n/2+1
不管是剩n/2还是n/2+1,只要某个子树达到了n/2大小,都进行一次操作:
找出y的其中一个子节点,断掉,与x连上。
此时不管y是否为重心,都可以保证重心唯一。
而如果没有出现大小为n/2的子树,那么重心一定唯一,此时删加同一条边即可。
#include <bits/stdc++.h> #include<vector> using namespace std; typedef long long ll; const int maxn = 1e5+10; vector<int>g[maxn]; int si[maxn]; int n; int ok = 0 ; int x,y; int dfs(int v,int f) { si[v]=1; for(int i=0;i<g[v].size();i++) { if(f!=g[v][i]) { dfs(g[v][i],v); si[v]+=si[g[v][i]]; } } if(si[v]==n/2) { x=v; y=f; ok=1; } } int main(){ int t; cin >> t; while(t--) { ok=0; scanf("%d",&n); for(int i=0;i<maxn;i++) { g[i].clear(); si[i]=0; } int a,b; for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); g[a].push_back(b); g[b].push_back(a); } dfs(1,0); if(!ok) { cout<<a<<" "<<b<<endl; cout<<a<<" "<<b<<endl; } else { for(int i=0;i<g[y].size();i++) { if(g[y][i]!=x) { cout<<g[y][i]<<" "<<y<<endl; cout<<g[y][i]<<" "<<x<<endl; break; } } } } }