题意:
对于一棵树 删除一条边再加上一条边使得新树只有一个重心
输入
输入由多个测试案例组成。第一行包含一个整数t(1≤t≤104)--测试案例的数量。测试用例的描述如下。
每个测试用例的第一行包含一个整数n(3≤n≤105)--顶点的数量。
接下来的n-1行中的每一行都包含两个整数x,y(1≤x,y≤n)。这意味着,存在一条连接顶点x和y的边。
可以保证给定的图是一棵树。
保证所有测试案例的n之和不超过105。
输出
对于每个测试案例,打印两行。
在第一行打印两个整数x1,y1(1≤x1,y1≤n),这意味着你切断了顶点x1和y1之间的边。应该存在连接顶点x1和y1的边。
在第二行打印两个整数x2,y2(1≤x2,y2≤n),这意味着你增加了顶点x2和y2之间的边。
这两次操作后的图形应该是一棵树。
如果有多个解决方案,你可以打印任何一个。
树的重心:只有当你切掉这个顶点(移除它并移除这个顶点的所有边)时,剩余图形的最大连接部分的大小才是最小的。
树的重心的一些性质:
1.删除重心后的所有子树,节点树都不超过原树的一半。
2.一颗树最多两个重心,而且相邻。
3.树中所有的节点到重心的距离之和最小,要是有两个重心,那么他们的距离相等。
4.这颗树要是删除或者增加一条边,重心最多只移动一条边。
题解:
分情况讨论
1.对于树如果是只有一个重心,那么随便取一个与他直连的点,断开然后再连接即可
2.如果有两个重心,根据重心的性质我们可以知道,这两个x,y重心必定相邻,那么我们就取其中一个重心x,找到其他与他直连的点z并且这个点和另一个重心y不相连,然后断开x与z,连接z与y即可
- #include<iostream>
- #include<algorithm>
- #include<map>
- #include<queue>
- #include<vector>
- #include<cstring>
- using namespace std;
- int vis[100050];
- vector<int> p[100050];
- int son[100060];
- int res = 0;
- int n;
- int t1 = 0,t2 = 0;
- int ans = 0;
- int dfs(int u)
- {
- vis[u] = 1;
- int sum = 1,res = 0;
- for(int i = 0;i < p[u].size();i++)
- {
- int j = p[u][i];
- if(!vis[j])
- {
- int s = dfs(j);
- sum += s;
- res = max(s,res);
- }
- }
- res = max(res,n - sum);
- if(ans >= res)
- {
- son[u] = res;
- t2 = t1;
- t1 = u;
- ans = res;
- }
- return sum;
- }
- void solve()
- {
- cin >> n;
- for(int i = 1;i <= n;i++)
- p[i].clear(),vis[i] = 0,son[i] = 0;
- res = 0; ans = 1e9;
- t1 = 0,t2 = 0;
- for(int i = 1;i < n;i++)
- {
- int l,r;
- cin >> l >>r;
- p[l].push_back(r);
- p[r].push_back(l);
- }
- dfs(1);
- if(son[t1] != son[t2])
- {
- cout<<t1<<" "<<p[t1][0]<<"\n";
- cout<<t1<<" "<<p[t1][0]<<"\n";
- }
- else
- {
- int u = 0;
- for(int i = 0;i < p[t1].size();i++)
- {
- int j = p[t1][i];
- if(j!=t2)
- {
- u = j;
- break;
- }
- }
- cout<<t1<<" "<<u<<"\n";
- cout<<t2<<" "<<u<<"\n";
-
- }
- }
- int main()
- {
- int t = 1;
- cin >> t;
- while(t--)
- {
- solve();
- }
- }
- //
- //