CF 1796 E. Colored Subgraphs

发布时间 2023-06-18 00:35:11作者: JustACommonMan

非常纯正的换根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;
}