BZOJ1509: [NOI2003]逃学的小孩

4 篇文章 0 订阅
4 篇文章 0 订阅

题目

bzoj1509

Input

第一行是两个整数N(3<=N<= 200000)和M,分别表示居住点总数和街道总数。以下M行,每行给出一条街道的信息。第i+1行包含整数Ui、Vi、Ti(1<=Ui, Vi<= N,1<= Ti<= 1000000000),表示街道i连接居住点Ui和Vi,并且经过街道i需花费Ti分钟。街道信息不会重复给出。

Output

仅包含整数T,即最坏情况下Chris的父母需要花费T分钟才能找到Chris。

Sample Input

4 31 2 12 3 13 4 1

Sample Output

4

题解

我们考虑最坏情况,一定是有折返的。由这么两种情况:
CASE1
CASE2
对于第一种情况,我们只要找从一个点出发的最长的三条不重叠路径即可。
设这三条路径长度为 a,b,c(abc) ,那么答案就是 a+2b+c
如何统一这两种情况?我们只要允许长度为0的路径就行了。
接下来进行树DP就行了。
两次dfs,一次求出子树中前1,2,3的路径。第二次再考虑父亲。同时还有注意记录路径是由哪个点得到的。


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;

#define N 200010
typedef long long ll;
struct edge{
    int x,next,d;
}e[N*2];
ll dis[N][3],ans;
int first[N],n,m,pre[N][3],x,y,z,tot;

inline void add(int x,int y,int z){
    e[++tot].d=z;
    e[tot].x=y;
    e[tot].next=first[x];
    first[x]=tot;
}

inline bool updata(int x,int y,ll d){
    if(d>dis[x][0]){
        dis[x][2]=dis[x][1]; pre[x][2]=pre[x][1];
        dis[x][1]=dis[x][0]; pre[x][1]=pre[x][0];
        dis[x][0]=d; pre[x][0]=y;
        return true;
    }else
    if(d>dis[x][1]){
        dis[x][2]=dis[x][1]; pre[x][2]=pre[x][1];
        dis[x][1]=d; pre[x][1]=y;
        return true;
    }else
    if(d>dis[x][2]){
        dis[x][2]=d,pre[x][2]=y;
        return true;
    }
    return false;
}

void dfs(int x, int y){
    for(int i=first[x];i;i=e[i].next)
    if(e[i].x!=y){
        dfs(e[i].x,x);
        updata(x,e[i].x,(ll)e[i].d+dis[e[i].x][0]);
    }
}

void dp(int x,int y,int d){
    for(int i=0;i<3;i++)
    if(pre[y][i]!=x)
    if(updata(x,y,(ll)d+dis[y][i]))break;
    for(int i=first[x];i;i=e[i].next)
    if(e[i].x!=y)dp(e[i].x,x,e[i].d);
}

char BUF[2000010],*buf,*end;
#define getch() (buf==end?fread(BUF,1,2000000,stdin),buf=BUF,end=buf+2000000,*(buf++):*(buf++))
inline void read(int &x){
    static char c;
    for(c=getch();c<'0'||c>'9';c=getch());
    for(x=0;'0'<=c&&c<='9';c=getch())x=x*10+c-'0';
}

int main(){
    read(n); read(m);
    for(int i=1;i<=m;i++){
        read(x); read(y); read(z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1,0); dp(1,0,0);
    for(int i=1;i<=n;i++)
    //printf("%lld %lld %lld\n",dis[i][0],dis[i][1],dis[i][2]);
    if(dis[i][0]+2*dis[i][1]+dis[i][2]>ans)
    ans=dis[i][0]+2*dis[i][1]+dis[i][2];
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值