Jzoj P1737 删边___dfs+树的直径+预处理

78 篇文章 0 订阅
9 篇文章 0 订阅

题目大意:

给出 N N 个点,N1条边的连通图.
现要求删除一条边,使得连通块的直径总和最大.所谓连通块的直径是指连通块中最远两点之间的距离。问直径总和最大是多少?

分析:

暴力就是每次拆掉一条边,然后暴力求解分成的 2 2 棵树的树上最长链的总和,不过我们发现,
可以预处理出每一棵子树的
①最大直径
②子树中的最远点,次远点,与次次远点与根的距离与所在的子树位置(任意2个结点不能在同一颗子树)
③直径最大的两颗子树的直径跟所在位置
然后,每次拆掉一条边,以后,分成的子树的两个根,设为 x,y x , y
那么它们相邻,我们设 y y x的儿子,
那么显然以 y y 位根子树的直径是已知的,即f[y]
而对以 x x <script type="math/tex" id="MathJax-Element-557">x</script>为根子树,分类讨论一下即可

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 100005

using namespace std;

struct Node {
    int To, w, nxt;
}e[2*N];
int f[N], g[N][3][2], c[N][2][2], Ls[N], n, ans, cnt = 0;

void Addedge(int u, int v, int w) {
    e[++cnt].To = v, e[cnt].w = w, e[cnt].nxt = Ls[u], Ls[u] = cnt; 
}

void dfs1(int x, int father) {
    for (int k = Ls[x]; k ; k = e[k].nxt) {
         if (e[k].To == father) continue;
         int y = e[k].To;
         dfs1(y, x);
         if (f[y] > c[x][1][1]) {
             memcpy(c[x][2], c[x][1], sizeof(c[x][1]));
             c[x][1][1] = f[y];
             c[x][1][2] = y;
         } else if (f[y] > c[x][2][1]) c[x][2][1] = f[y], c[x][2][2] = y;

         int z = g[y][1][1] + e[k].w;        
         if (z > g[x][1][1]) {
             memcpy(g[x][3], g[x][2], sizeof(g[x][2]));
             memcpy(g[x][2], g[x][1], sizeof(g[x][1]));
             g[x][1][1] = z, g[x][1][2] = y;
         } else if (z > g[x][2][1]) {
                    memcpy(g[x][3], g[x][2], sizeof(g[x][2]));
                    g[x][2][1] = z, g[x][2][2] = y;
         } else if (z > g[x][3][1]) {
                    g[x][3][1] = z, g[x][3][2] = y;
         }
    }
    f[x] = max(c[x][1][1], g[x][1][1] + g[x][2][1]);
}

void dfs2(int x, int father, int cmax, int len) {
    if (x != 1) ans = max(ans, f[x] + cmax);
    for (int i = Ls[x]; i ; i = e[i].nxt) {
         if (e[i].To == father) continue;
         int y = e[i].To, a1, a2, a3;
         if (g[x][1][2] == y) {
             a1 = g[x][2][1];
             a2 = g[x][2][1] + g[x][3][1];
         } else if (g[x][2][2] == y) {
                    a1 = g[x][1][1];
                    a2 = g[x][1][1] + g[x][3][1];
         } else {
                    a1 = g[x][1][1];
                    a2 = g[x][1][1] + g[x][2][1];
         }
         if (c[x][1][2] == y) a3 = c[x][2][1]; 
                         else a3 = c[x][1][1];
         dfs2(y, x, max(max(cmax, a2), max(a3, a1 + len)), max(a1, len) + e[i].w);
    }
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
         int u, v, w;
         scanf("%d %d %d", &u, &v, &w);
         Addedge(u, v, w);
         Addedge(v, u, w);
    }
    dfs1(1, -1);
    ans = f[1];
    dfs2(1, -1, 0, 0);
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值