题目大意:转译过来就是 给一棵 n 个点有根树,按升序输出所有点为根节点的子树的所有重心。
徐州现场因为这题体验直接拉满,前4个小时签到打崩了直接拉队友陪葬(三个人看了4个小时A和F,全是我的锅),封榜后和队友开这题,第一眼完全没思路,但是有一种知识量全在掌握范围之内有机会做出来的直觉。怀着这种直觉我决定最后一个小时要用上我全部的知识来绝杀这题。由于之前在jxccpc做过式子定义相同的题已经知道就是要求重心,队友直接为我搬出了树的重心的所有可能用到的性质(期间多次想起点分治,因为分治过程就是一个找重心的过程,不过这个过程会破坏树形,每次放下又因为没有思路而回想起来),正在对这些性质逐条斟酌的时候(期间提过一次希望能用类似合并两棵树的方法来搞),队友突然补充了一句,如果两棵树合并新的重心会在这两个在重心的链上,结合一下自己的经验想到了做法。
(此时我已确定这个做法可行,已经开始想要如何实现了,嘴里却一直在问队友觉得这个方法如何,希望队友能得到肯定的答案而和我产生共鸣加速思考实现的过程,但队友因为不太熟悉子树合并的过程遂放弃)
然后就是我们队2019年赛季最惨场面了,在剩余大概18分钟的时候已经按照大概的思路码完。因为代码出乎意料的好写,又一次激起了我全部的希望,希望能在接下来18分钟内调试完。回想起来徐州那一场状态非常奇怪,先是两道签到题代码频繁出错,漏写else 导致队友白看2个小时没有开到E题,另一名队友则和我打表打到比赛结束,这题写完发现逻辑判断好多写反了。最后的关头发现这个错误时开始紧张了起来,预感可能会调不出来死在某些小方面。开始了长达15分钟的逻辑check。
最后的最后,交了三发都wa了,此时只剩不到5分钟(大概2-3分钟),脑子一片混乱,队友多次提到某个地方少了一个else 会不会出现不更新的情况(又少了一个else),被我忽略,最终没过此题。
(就是此处的else)
准备颁奖时突然脑子里突然闪过这个地方(回光返照?),猛地明白了队友的多次疑问,心态开始瓦解,赛后用这个方法尝试群友给的原题,果然能过。
这一场锅直接拉满,幸好队友心态都不错没有责怪我,在火车上整理了一下这场因为我的问题错失了稳稳的银牌(南昌也错失了好可惜)。
此前比赛会因为太在乎自己的表现力而紧张,现场降智严重(易受外界干扰),在2019年多校和网络赛以及第一次CCPC铁首和厦门之挫,终于能坦白接受自己的失败。(结果似乎因为越来越随意而打崩了所有的比赛)我的2019年所有ICPC,CCPC参赛全部结束,没能取得理想的成绩。
回归正题:由于两棵子树合并,他们的重心一定在两个重心的连线上,一棵树最多两棵重心,且一定挨着(这题几乎用上了所有的重心的性质,从这个做法出发是一道不错的题)。
一个做法是,采用树形dp的思路,通过不断合并子树来合并两个重心。这里要解决两个问题:
1.如何合并两个重心
2.题目问的是所有重心(一棵树最多两个),但是每次合并两棵重心代码必然繁琐无比
对于第一个问题:既然新的重心一定在两棵子树到当前根节点的链上,那么新的重心的位置只有两种可能,要么在根节点上,要么在两端的链上。可以通过不断将重心往根节点方向爬,深度较大的点一定是重心。
什么条件下重心可以往上爬: 设当前重心为u,根节点为rt,sz[u] 是 u 结点的为根节点的子树的结点个数。若u往上爬,u的所有子节点到u的距离会加一,其它点到u的距离会减一,根据题目给的计算式,这个该变量必须是负值才可以往上爬:那么得到一个条件是:sz[rt] - sz[u] > sz[u],满足这个条件 u 就可以向上爬,直到 u == rt 或者 条件不满足
两棵重心可以通过这个方法先寻找到深度较大的那个,另外一个必然是它的父结点(或者找到深度较小的那个,另外一个必然是它的子节点),找完所有点的重心后再处理一遍即可:
赛后重写的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
vector<int> g[maxn],h[maxn];
int n,f[maxn],p[maxn],son[maxn],deep[maxn];
int upd(int rt,int x,int y) {
while(deep[x] > deep[rt] && f[rt] - f[x] > f[x]) {
x = p[x];
}
while(deep[y] > deep[rt] && f[rt] - f[y] > f[y]) {
y = p[y];
}
//printf("%d %d %d\n",rt,x,y);
if(deep[x] > deep[y]) son[rt] = x;
else son[rt] = y;
}
void dfs(int u,int fa) {
f[u] = 1,son[u] = u;
p[u] = fa;
for(auto it : g[u]) {
if(it == fa) continue;
deep[it] = deep[u] + 1;
dfs(it,u);
f[u] += f[it];
upd(u,son[u],son[it]);
}
}
int main() {
scanf("%d",&n);
for(int i = 1; i < n; i++) {
int u,v;scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
deep[1] = 1;
dfs(1,0);
for(int i = 1; i <= n; i++) {
h[i].push_back(son[i]);
if(p[son[i]] && f[i] - f[son[i]] == f[son[i]])
h[i].push_back(p[son[i]]);
sort(h[i].begin(),h[i].end(),less<int>());
for(int j = 0; j < h[i].size(); j++) {
printf("%d%s",h[i][j],j == h[i].size() - 1 ? "\n" : " ");
}
}
return 0;
}