[BZOJ4379][POI2015]Modernizacja autostrady

2022-10-18

题目描述

给定一棵无根树,边权都是\(1\),请去掉一条边并加上一条新边,定义直径为最远的两个点的距离,请输出所有可能的新树的直径的最小值和最大值。

input

第一行包含一个正整数$ n(2<=n<=500000) $,表示这棵树的点数。

接下来\(n-1\)行,每行包含两个正整数\(u,v(1<=u,v<=n)\),表示\(u\)\(v\)之间有一条边。

output

第一行输出五个正整数\(k,x_1,y_1,x_2,y_2\),其中k表示新树直径的最小值,\(x_1,y_1\)表示这种情况下要去掉的边的两端点,\(x_2,y_2\)表示这种情况下要加上的边的两端点。

第二行输出五个正整数\(k,x_1,y_1,x_2,y_2\),其中k表示新树直径的最大值,\(x_1,y_1\)表示这种情况下要去掉的边的两端点,\(x_2,y_2\)表示这种情况下要加上的边的两端点。

若有多组最优解,输出任意一组。

sample input

6
1 2
2 3
2 4
4 5
6 5

sample output

3 4 2 2 5
5 2 1 1 6

树形\(dp\)题。

很显然我们需要对于每条边的断开分类讨论。

对于一条边,断开后分为两个连通块\(a\),\(b\)

记两个连通块的直径为\(d_a,d_b\)

对于最小值很显然就是连接两条直径中点或两条直径,即
\(max(max(d_a,d_b),1+(d_a+1)/2+(d_b+1)/2)\)

而对于最大值,我们只用连接两条直径的端点即可。

现在,问题变成了怎么快速的求出上述的\(d_a,d_b\)

这是一颗树

对于节点\(a\)来说,上述的\(d_a\)为在该节点子树内的最大的直径。

\(d_b\),为不在该点子树内的最大直径。

我们又将问题转换为了求每个节点的在该节点子树内的最大直径以及不在该节点子树内的最大直径。

我们该如何快速的求出每个节点的值呢?

我们需要维护每个节点的前三子链(不同的儿子),向上最长链以及前二最大子节点的子树直径。

很显然,一个节点的子树内直径为前两个子链之和,或最大的子节点的子树直径。这样要用子树更新节点。

而一个节点的不在该点子树内的直径为,其父亲的该值或其父亲的向上最长链加除他外父亲的最长子链,或其父亲除它外的前两子链和。
这里要用父亲节点更新子节点。

两次\(dfs\)树形\(dp\)即可。

#include <cstdio>
#include <cstring>
#include <map>
#include <set>
#include <iostream>
#include <algorithm>
 
using namespace std;
 
#define ll long long
#define u64 unsigned long long
#define reg register
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,g,x) for(reg int i=(g).head[x]; i; i=(g).nxt[i])
 
inline int read() {
    int res = 0, f = 1;
    char c;
    while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
    do res = (res << 3) + (res << 1) + (c ^ 48);
    while (c = getchar(), c >= 48 && c <= 57);
    return f ? res : -res;
}
 
template<class t>inline bool min(t &a, t const&b) {
    return a > b ? a = b, 1 : 0;
}
template<class t>inline bool max(t &a, t const&b) {
    return a < b ? a = b, 1 : 0;
}
 
const int n = 5e5 + 5, m = 5e5 + 5, mod = 1e9 + 9;
 
int n;
 
struct link_list {
    int tot, nxt[m << 1], to[m << 1], head[n];
    inline void addedgepair(int a, int b) {
        to[++tot] = b, nxt[tot] = head[a], head[a] = tot;
        to[++tot] = a, nxt[tot] = head[b], head[b] = tot;
    }
} g;
 
int fa[n], dep[n], tree_sond[n], tree_unsond[n], list_son[n][4], list_unson[n], mason_d[n][3];
 
void dfs1(int x, int pre) {
    fa[x] = pre, dep[x] = dep[pre] + 1;
    erep(i, g, x) {
        int y = g.to[i];
        if (y == pre)continue;
        dfs1(y, x);
        reg int pos = 3;
        int cnt = list_son[y][0] + 1;
        for (pos = 3; pos && list_son[x][pos - 1] < cnt; pos--)list_son[x][pos] = list_son[x][pos - 1];
        list_son[x][pos] = cnt;
        cnt = tree_sond[y];
        for (pos = 2; pos && mason_d[x][pos - 1] < cnt; pos--)mason_d[x][pos] = mason_d[x][pos - 1];
        mason_d[x][pos] = cnt;
    }
    tree_sond[x] = max(mason_d[x][0], list_son[x][0] + list_son[x][1]);
}
 
void dfs2(int x, int pre) {
    erep(i, g, x) {
        int y = g.to[i];
        if (y == pre)continue;
        int pos1, pos2;
        pos1 = (list_son[x][0] == list_son[y][0] + 1);
        list_unson[y] = max(list_son[x][pos1], list_unson[x]) + 1;
        pos1 = (tree_sond[y] == mason_d[x][0]);
        max(tree_unsond[y], max(tree_unsond[x], mason_d[x][pos1]));
        if (list_son[y][0] + 1 == list_son[x][0])pos1 = 1, pos2 = 2;
        else if (list_son[y][0] + 1 == list_son[x][1])pos1 = 0, pos2 = 2;
        else pos1 = 0, pos2 = 1;
        max(tree_unsond[y], max(list_son[x][pos1] + list_son[x][pos2], list_son[x][pos1] + list_unson[x]));
        dfs2(y, x);
    }
}
 
inline int mid(int x, int y, int len) {
    if (dep[x] < dep[y])swap(x, y);
    len /= 2;
    rep(i, 1, len)x = fa[x];
    return x;
}
 
int dis[n], ans;
 
void dfs3(int x, int pre, int f) {
    dis[x] = dis[pre] + 1;
    if (dis[ans] < dis[x])ans = x;
    erep(i, g, x) {
        int y = g.to[i];
        if (y == pre || y == f)continue;
        dfs3(y, x, f);
    }
}
 
inline int farest(int x, int y) {
    memset(dis, 0, sizeof dis);
    ans = x;
    dfs3(x, 0, y);
    return ans;
}
 
int mi[n], ma[n];
 
inline void debug(void) {
    rep(i, 1, n)printf("%d %d %d %d %d\n", tree_sond[i], tree_unsond[i], fa[i], list_unson[i], dep[i]);
}
 
int _main(void) {
    int n = read(), a, b;
    ret(i, 1, n) {
        a = read(), b = read();
        g.addedgepair(a, b);
    }
    dfs1(1, 0); dfs2(1, 0);
    int ans1 = 1, ans2 = 1;
    mi[1] = ma[1] = tree_sond[1];
    rep(i, 2, n) {
        int res1 = tree_sond[i] + tree_unsond[i] + 1;
        int res2 = max(max(tree_sond[i], tree_unsond[i]), 1 + (tree_unsond[i] + 1 ) / 2 + (tree_sond[i] + 1) / 2);
        mi[i] = res2, ma[i] = res1;
        if (ma[ans1] < ma[i])ans1 = i;
        if (mi[ans2] > mi[i])ans2 = i;
    }
    if (ans2 == 1)printf("%d %d %d %d %d\n", mi[1], a, b, a, b);
    else {
        printf("%d %d %d ", mi[ans2], ans2, fa[ans2]);
        int x1 = farest(ans2, fa[ans2]), y1 = farest(x1, fa[ans2]), x2 = farest(fa[ans2], ans2), y2 = farest(x2, ans2);
        printf("%d %d\n", mid(x1, y1, tree_sond[ans2]), mid(x2, y2 , tree_unsond[ans2]));
    }
    if (ans1 == 1)printf("%d %d %d %d %d\n", ma[1], a, b, a, b);
    else printf("%d %d %d %d %d\n", ma[ans1], ans1 , fa[ans1], farest(ans1, fa[ans1]), farest(fa[ans1], ans1));
    return 0;
}
 
int main() {
#define offline1
#ifdef offline
    freopen(".in", "r", stdin);
    freopen(".out", "w", stdout);
    _main();
    fclose(stdin); fclose(stdout);
#else
    _main();
#endif
    return 0;
}

《[BZOJ4379][POI2015]Modernizacja autostrady.doc》

下载本文的Word格式文档,以方便收藏与打印。