[树形DP][暴力]删边

题目描述

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

Input
  文件名为 delete.in
  第一行正整数N.
  接下来N-1行.每行两个数,A,B,LEN表示A,B(1<=A,B<=N)有一条长度为Len(1<=Len<=1000)的边连接着.

Output
  文件名为 delete.out
  一个数Ans.直径总和的最大值.

Sample Input
10
2 1 982
3 1 169
4 1 934
5 1 325
6 1 735
7 1 675
8 2 302
9 3 450
10 5 173

Sample Output
2668

Data Constraint
30% N<=100
70% N<=5000
100% N<=100000

分析

首先我们发现对于一棵树,它的直径有两种情况:
1、不经过根节点,只经过它的一棵子树
2、经过根节点,经过两棵子树中
那么容易得知
fi表示以i为根的子树的直径
fi=max(最远点和次远点到根的距离之和,i的子树中的最大直径)
所以我们暂时可以确定我们需要这几个值:
以i为根的点距离i最远的点和次远的点
i的子树的最大直径
(记录一下这些值从哪来)
然后对于删边,如果我们删掉
u为出发点,v为结束点的边
首先f[v]已经明确了(预处理)
然后外面的直径有四种情况:
1、这个直径不经过i的子树,直接来自之前的搜索过程
2、这个直径经过i点跨越两棵子树
3、这个直径在i的一棵子树中
4、这个直径一部分在i中,一部分在i外
①简单,记录即可
②我们需要用之前的取的最远点和次远点得到这个直径,那么问题来了:
如果最远点或次远点在要断开的联通块里怎么办?
这时候我们要在预处理里加一个次次远点,备用这种情况
③这个也简单,我们记录了i的最大子树
同样也有上面的问题,所以要记录次大子树
④也比较简单,记录之前的最大直径,和i的最远点(次远点)相比较即可

#include <iostream>
#include <cstdio>
#include <memory.h>
#define rep(i,a,b) for (i=a;i<=b;i++)
const int N=100001;
using namespace std;
int n;
struct Edge {
    int u,v,w,nx;
}g[2*N];
int cnt,list[N];
int f[N],h[N][3][2],hf[N][2][2];
int ans;

void Addedge(int u,int v,int w) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].w=w;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].w=w;g[cnt].nx=list[v];list[v]=cnt;
}

void Prepare(int u,int fa) {
    if (!list[u])
    h[u][0][0]=u;
    for (int i=list[u];i;i=g[i].nx)
    if (g[i].v!=fa) {
        Prepare(g[i].v,u);
        f[u]=max(f[u],f[g[i].v]);
        if (f[g[i].v]>hf[u][0][1]) {
            hf[u][1][1]=hf[u][0][1];
            hf[u][0][1]=f[g[i].v];
            hf[u][1][0]=hf[u][0][0];
            hf[u][0][0]=g[i].v;
        }
        else
        if (f[g[i].v]>hf[u][1][1]) {
            hf[u][1][1]=f[g[i].v];
            hf[u][1][0]=g[i].v;
        }
        int x=h[g[i].v][0][1]+g[i].w;
        if (x>h[u][0][1]) {
            h[u][2][1]=h[u][1][1];
            h[u][2][0]=h[u][1][0];
            h[u][1][1]=h[u][0][1];
            h[u][1][0]=h[u][0][0];
            h[u][0][1]=x;
            h[u][0][0]=g[i].v;
        }
        else
        if (x>h[u][1][1]) {
            h[u][2][1]=h[u][1][1];
            h[u][2][0]=h[u][1][0];
            h[u][1][1]=x;
            h[u][1][0]=g[i].v;
        }
        else
        if (x>h[u][2][1]) {
            h[u][2][1]=x;
            h[u][2][0]=g[i].v;
        }
    }
    f[u]=max(hf[u][0][1],h[u][0][1]+h[u][1][1]);
}

void Dfs(int u,int upper,int line,int fa) {
    if (fa!=-1) ans=max(ans,f[u]+upper);
    for (int i=list[u];i;i=g[i].nx)
    if (g[i].v!=fa) {
        int s1,s2,s3;
        if (h[u][0][0]==g[i].v) {
            s1=h[u][1][1];
            s2=h[u][1][1]+h[u][2][1];
        }
        else {
            s1=h[u][0][1];
            if (h[u][1][0]==g[i].v) s2=h[u][0][1]+h[u][2][1];
            else s2=h[u][0][1]+h[u][1][1];
        }
        if (hf[u][0][0]==g[i].v)
        s3=hf[u][1][1];
        else s3=hf[u][0][1];
        Dfs(g[i].v,max(max(upper,s3),max(s1+line,s2)),max(s1,line)+g[i].w,u);
    }
}

int main() {
    int i;
    scanf("%d",&n);
    rep(i,1,n-1) {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        Addedge(u,v,w);
    }
    Prepare(1,-1);
    Dfs(1,0,0,-1);
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值