魏传之长坂逆袭

题目描述
众所周知,刘备在长坂坡上与他的一众将领各种开挂,硬生生从曹操手中逃了出去,随后与孙权一起火烧赤壁、占有荆益、成就霸业。而曹操则在赤壁一败后再起不能,终生无力南下。
建安二十五年(220年),曹操已到风烛残年,但仍难忘当年长坂的失误,霸业的破灭。他想如果在刘备逃亡的路中事先布下一些陷阱,便能拖延刘备的逃脱时间了。你作为曹操身边的太傅,有幸穿越到了208年的长坂坡,为大魏帝国贡献一份力,布置一些陷阱。但时空守卫者告诉你你不能改变历史,不能拖增大刘备的最大逃脱时间,但你身为魏武之仕,忠心报国,希望能添加一些陷阱使得刘备不论怎么逃跑所用的时间都一样。
已知共有n个据点,n-1条栈道,保证据点联通。1号据点为刘备军逃跑的起点,当刘备军跑到某个据点后不能再前进时视为刘备军逃跑结束。在任意一个栈道上放置1个陷阱会使通过它的时间+1,且你可以在任意一个栈道上放置任意数量的陷阱。
现在问你在不改变刘备军当前最大逃跑时间的前提下,需要添加最少陷阱,使得刘备军的所有逃脱时间都尽量的大。

输入
第一行一个数n,表示据点个数。
接下来n-1行每行三个数,ai、bi、ti,表示从据点ai通过第i个栈道到bi耗时ti

输出
仅包含一个数,为需要添加的最少陷阱数。

样例输入
3
1 2 1
1 3 3
样例输出
2

提示
【数据规模和约定】
对于 5%的数据,1<=n<=100000,1<=ti<=200000
对于 100%的数据,1<=n<=500000,0<ti<=1000000

手残系列2。。。这道题的意思就是先找出原树中一条长度最大的链,然后要添加最少的数,使得所有链的长度都等于最大链。
那么yy一会儿可以发现一个性质:要是能加的话,肯定先加上面的,因为上面的对下面的都有添加的效果,那么递归一下,就可以算出每个节点最大能加多少。
接下来就脑残+手残了。写完以后,测了几个只有两三层的小数据都对了,就满心以为ok了(这其中也包括第二题位运算括号漏加的因素,导致我一直在调T2)。于是T1愉快的爆0了。。。
晚上我发现,添加的效果是会累积的,也就是说,我添加的话,必须再用另外一个数组来记录当前已被上面累加了多少。。。
数据测试很要紧啊(我的内心是崩溃的)。。。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,x,y,z,tot,TOT,Maxdeep;
ll ans,Max;
int head[500005],Next[1000005],to[1000005],len[1000005];
int HEAD[500005],NEXT[500005],TO[500005];
int deep[500005];
ll c[500005],d[500005];
void add(int x,int y,int z)
{
    tot++;
    Next[tot]=head[x];
    to[tot]=y;
    len[tot]=z;
    head[x]=tot;
}
void ADD(int x,int y)
{
    TOT++;
    NEXT[TOT]=HEAD[x];
    TO[TOT]=y;
    HEAD[x]=TOT;
}
void dfs(int k,int pre,ll s)
{
    deep[k]=deep[pre]+1;
    ADD(deep[k],k);
    Maxdeep=max(Maxdeep,deep[k]);
    Max=max(Max,s);
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) dfs(to[i],k,s+len[i]);
}
void dp(int k,int pre,ll s)
{
    c[k]=Max-s;
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        dp(to[i],k,s+len[i]);
        c[k]=min(c[k],c[to[i]]);
    }
}   
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        head[i]=-1;
        HEAD[i]=-1;
    }
    for(int i=1;i<n;i++) 
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0,0);
    dp(1,0,0);
    for(int i=1;i<=Maxdeep;i++) 
    {
        for(int j=HEAD[i];j!=-1;j=NEXT[j]) 
        {
            x=TO[j];
            ans=ans+c[x]-d[x];
            for(int k=head[x];k!=-1;k=Next[k]) d[to[k]]=d[to[k]]+c[x];
        }
    }
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值