HDU 4679 Terrorist’s destroy (拆边+树的直径)

给一棵树,每条边上都有一个权值,去掉树上任意一条边之后,分成两个子树,两个子树的最长路与这条边上的权值相乘,的到一个乘积。问去掉那一条边可以使这个乘积最小。

首先找到树上的最长路,那么删边的时候有两种情况:

1. 这条边不是最长路上的边

2. 这条边是最长路上的边

对于第一种情况,很容易计算出乘积。

对于第二种情况,只需要计算出被分成的两个子树里面的最长路径长度即可,这个可以预处理一下。分析可以发现,这种情况下,两棵子树的最长路,一定是以整棵树的最长路的两个端点为起始点的,因此只需要预处理出所有点到两个端点的距离,然后根据删除的最长路边求两颗子树中的最大值即可。

所有预处理都是dfs……

My code:

#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100100
int n;
int head[N],cnt,len,ans,id;
int dis_s[N],dis_t[N],mx_s[N],mx_t[N];
struct Edge
{
    int next,to,w,id;
}e[N*2];
void add_edge(int a,int b,int w,int id)
{
    e[cnt].id=id;
    e[cnt].to=b;
    e[cnt].w=w;
    e[cnt].next=head[a];
    head[a]=cnt++;
}
void dfs(int u, int fa, int dis[])
{
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        int v=e[i].to;
        if(v!=fa)
        {dis[v]=dis[u]+1;dfs(v,u,dis);}
    }
}
void get_max(int u, int fa, int mx[],int dis[])
{
    mx[u]=dis[u];
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        int v=e[i].to;
        if(v!=fa)
        {
            get_max(v,u,mx,dis);
            mx[u]=max(mx[u],mx[v]);
        }
    }
}
void dfs2(int u, int fa)
{
    for(int i = head[u]; i != -1; i = e[i].next)
    {
        int v=e[i].to;
        if(v!=fa)
        {
            int w;
            if(dis_s[u]+dis_t[v]+1==len) w=e[i].w*max(mx_s[u],mx_t[v]);
            else w=e[i].w*len;
            if(w<ans) {ans=w;id=e[i].id;}
            else if(w==ans) id=min(id,e[i].id);
            dfs2(v,u);
        }
    }
}
int main()
{
    int T;scanf("%d",&T);
    for(int g = 1; g <= T; g++)
    {
        scanf("%d",&n);
        memset(head,-1,sizeof(head));
        cnt=0;
        for(int i = 1; i < n; i++)
        {
            int a,b,w;scanf("%d%d%d",&a,&b,&w);
            add_edge(a,b,w,i);
            add_edge(b,a,w,i);
        }
        dis_s[1]=0;
        dfs(1,-1,dis_s);
        int s=1;
        for(int i = 1; i <= n; i++)if(dis_s[s]<dis_s[i])s=i;
        dis_s[s]=0;
        dfs(s,-1,dis_s);
        len=dis_s[1];
        int t=1;
        for(int i = 1; i <= n; i++)if(dis_s[t]<dis_s[i]){t=i;len=dis_s[t];}
        dis_t[t]=0;
        dfs(t,-1,dis_t);
        get_max(t,-1,mx_s,dis_s);
        get_max(s,-1,mx_t,dis_t);
        ans=0x7ffffff;
        dfs2(s,-1);
        printf("Case #%d: %d\n",g,id);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值