HDU 5886 Tower Defence (最长链预处理)

题意:给出一棵树,边上有权值,现在毁掉任意一条边,分成两部分,求这两部分中最远的两点距离期望,答案*(n-1)

分析:
说白了就是求最远距离加成和
对于一棵树来说,两点最远的距离,考虑两点:
1.最长链没有被拆开,那么答案就是最长链;
2.最长链被拆开了,那么答案一定在最长链的端点到某个叶子结点上;
证明略。

有了这个结论,这个题就好做了,首先预处理出最长链,数组存下来,然后对于每个最长链上的结点预处理出它的权值最大的叶子结点(不在最长链上),剩下的就是处理最长链左边和右边的拆开后的最大值了,左右扫一遍,两个数组LR存下来,最后On扫一遍最长链,取LR中最大值得到答案。
复杂度On

比赛写的急,代码有些挫

#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 200014


long long L[maxn],R[maxn];
int fir[maxn],nex[maxn],v[maxn],w[maxn],e_max;
int p[maxn],len,vis[maxn];
long long cost[maxn],cost1[maxn];

void init()
{
    len=0;
    memset(L,0,sizeof L);
    memset(R,0,sizeof R);
    memset(cost,0,sizeof cost);
    memset(cost1,0,sizeof cost1);
    memset(vis,0,sizeof vis);
    memset(fir,-1,sizeof fir);
    e_max=0;
}

void add_edge(int s,int t,int c)
{
    int e=e_max++;
    v[e]=t;
    w[e]=c;
    nex[e]=fir[s];
    fir[s]=e;
}

int temp,add;
void dfs1(int k,long long sum,int pre)
{
    if(sum>add) temp=k,add=sum;
    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(e==pre) continue;
        dfs1(e,sum+w[i],k);
    }
}

int ok;
int dfs2(int s,int t,int pre,long long sum)
{
    if(s==t)
    {
        ok=1;
        return t;
    }
    for(int i=fir[s];~i;i=nex[i])
    {
        int e=v[i];
        if(e==pre) continue;
        int tag=dfs2(e,t,s,sum+w[i]);
        if(ok)
        {
            vis[tag]=1;
            cost[len]=sum+w[i];
            p[len++]=tag;
            return s;
        }
    }
    return s;
}

long long dfs3(int k,int pre)
{
    long long mx=0;
    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(vis[e]||e==pre) continue;
        mx=max(mx,dfs3(e,k)+w[i]);
    }
    return mx;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        int n;
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int a,b,w;
            scanf("%d%d%d",&a,&b,&w);
            add_edge(a,b,w);
            add_edge(b,a,w);
        }
        add=0;dfs1(1,0,-1);
        int s=temp;
        add=0;dfs1(s,0,-1);
        int t=temp;
        long long sd=add;
        long long ans=0;
        ok=0;
        dfs2(s,t,-1,0);
        p[len++]=s;
        vis[s]=1;
        for(int i=1;i<len-1;i++)
        {
            cost1[i]=dfs3(p[i],-1);
        }
        ans+=sd*(n-len);
        long long temp=0,ss=0;
        for(int i=0;i<len;i++)
        {
            L[i]=max(sd-cost[i]+cost1[i],temp);
            temp=max(L[i],temp);
        }
        temp=0;
        for(int i=len-1;i>=0;i--)
        {
            R[i]=max(cost[i]+cost1[i],temp);
            temp=max(R[i],temp);
        }
        for(int i=0;i<len-1;i++)
        {
            ans+=max(L[i],R[i+1]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值