非常纯正的换根DP,都写在注释里了
题目链接:https://codeforces.com/contest/1796/problem/E
代码链接:https://codeforces.com/contest/1796/submission/209931430
/*
题意:将一棵树划分成若干条不相交的链(“不相交”指每个结点在且仅在一条链),求所有划分中,最短链长度得最大值
当该树是有根树时,自底向上找链,对于结点x,最优情况是将结点x与它的子结点y中最短的一条链连接,而其他的结点y对应的链不会继续增长了,需要将其记录
然后进行换根DP
换根x -> y:(分成两部分讨论:以结点y为根的子树 以及 去掉前者所剩下的部分)
f[x]表示以结点x为顶点的链的长度
mini[x] 表示结点x的子结点y的最小f[y]值
semini[x] 表示结点x的子结点y的最小f[y]值
对于结点x:
少了一条以结点y为顶点的链,当且仅当f[y] == mini[x]会对答案产生影响
影响为 删除f[x]链,删除semini[x]链,增加f[x] + semini[x]链,增加f[y]链
其他的 mini[x] semini[x] 不需要更新,因为它们对后续的递归过程不产生影响(也就是对以结点y为根的子树的答案不产生影响)
对于结点y:
多了一条以结点x为顶点的链,分为两种情况:
f[x] < mini[y]:
删除f[y]链,删除f[x]链,增加f[x] + 1链
增加mini[y]链(注意判断mini[y] != INF)
semini[y] = mini[y]
mini[y] = f[x]
f[x] < semini[y]:
增加f[x]链(这条链对于整棵树来讲,在对于结点x的讨论中,已经考虑过贡献了,此处不必再次考虑)
semini[y] = f[x]
用一个multiset维护即可,每个结点作为根结点时的答案就是multiset中的最小值
*/
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
void solve()
{
int n; cin >> n;
vector<int> e[n + 1];
for(int i = 1; i < n; i++){
int x, y; cin >> x >> y;
e[x].emplace_back(y), e[y].emplace_back(x);
}
vector<int> f(n + 1);
vector<int> mini(n + 1, INF), semini(n + 1, INF);
multiset<int> link;
function<void(int, int)> dfs1 = [&] (int x, int fa){
for(auto y : e[x]){
if(y == fa) continue;
dfs1(y, x);
if(f[y] < mini[x]) semini[x] = mini[x], mini[x] = f[y];
else if(f[y] < semini[x]) semini[x] = f[y];
}
f[x] = (mini[x] == INF ? 0 : mini[x]) + 1;
auto it = link.find(mini[x]);
if(it != link.end()) link.erase(it);
link.insert(f[x]);
};
dfs1(1, -1);
int ans = 0;
function<void(int, int)> dfs2 = [&] (int x, int fa){
ans = max(ans, *link.begin());
for(auto y : e[x]){
if(y == fa) continue;
vector<int> del, add; //用来回溯
int t1 = f[x], t2 = f[y], t3 = mini[y], t4 = semini[y];
if(f[y] == mini[x]){ //考虑结点x
auto it = link.find(f[x]);
if(it != link.end()) link.erase(it), del.emplace_back(f[x]);
it = link.find(semini[x]);
if(it != link.end()) link.erase(it), del.emplace_back(semini[x]);
f[x] = (semini[x] == INF ? 0 : semini[x]) + 1;
link.insert(f[x]), add.emplace_back(f[x]);
link.insert(f[y]), add.emplace_back(f[y]);
}
if(f[x] < mini[y]){
auto it = link.find(f[y]);
if(it != link.end()) link.erase(it), del.emplace_back(f[y]);
it = link.find(f[x]);
if(it != link.end()) link.erase(it), del.emplace_back(f[x]);
f[y] = f[x] + 1;
link.insert(f[y]), add.emplace_back(f[y]);
if(mini[y] != INF) link.insert(mini[y]), add.emplace_back(mini[y]);
semini[y] = mini[y];
mini[y] = f[x];
}else if(f[x] < semini[y]){
semini[y] = f[x];
}
dfs2(y, x);
//回溯
f[x] = t1, f[y] = t2, mini[y] = t3, semini[y] = t4;
for(auto t : del) link.insert(t);
for(auto t : add){
auto it = link.find(t);
if(it != link.end()) link.erase(it);
}
}
};
dfs2(1, -1);
cout << ans << endl;
}
int main(void)
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T = 1; cin >> T;
while(T--) solve();
return 0;
}