xsy 1790 - 不回头的旅行

本文介绍了一种使用点分治算法解决特定图论问题的方法:即如何在给定的一棵树中找到能经过最多节点的路径。通过巧妙地利用深度优先搜索技巧,文章详细解析了算法的具体实现过程,并附带完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

from NOIP2016模拟题28

Description

一辆车,开始没油,可以选择一个点(加油站)出发
经过一个点i可加g[i]的油,走一条边减少len的油
没油的时候车就跪了
特别的,跪在加油站上可以加油继续走
给出一棵树,求最多可以走过多少个点

Analysis

点分治很明显的,只是dfs要点技巧
求出从根往下走到某个点的深度及需要的油
求出从某个点往上走到根的深度及提供的油
注意要同样的两个端点,不同方向走答案不同
所以点分时要扫两次

向下

每个点到根的消耗/剩余会长得像心电图
1086046-20170203184009667-12180473.png
我们只要保证最低点>=0就好

向上

我们记录一个mx表示每个点到上一个可到根的点的消耗/剩余
每次从mx=0的可到根点x出发会经过如图
1086046-20170203184020354-1338954490.png
轨迹到达下一个mx>=0的点
则那个点可以到x
(旋转180°,改下0点)就变成从下面出发了
同时要把mx变回0

Code

#include <cstdio>
#include <cstdlib>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
const int INF=2147483647;
const int M=100007;

inline int rd(){
    int x=0;bool f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(;isdigit(c);c=getchar()) x=x*10+c-48;
    return f?x:-x;
}

int n;
int val[M];

int g[M],te;
struct edge{int y,d,next;}e[M<<1];
void addedge(int x,int y,int z){
    e[++te].y=y;e[te].d=z;e[te].next=g[x];g[x]=te;
}

struct node{
    int y,d;
    node(int yy=0,int dd=0){y=yy;d=dd;}
}que[M];
int tq;

int vis[M];
int sz[M];
int mi,size,rt;
int a[M];

int ans=0;

void getsz(int x,int fa){
    sz[x]=1;
    int p,y;
    for(p=g[x];p;p=e[p].next)
    if(!vis[y=e[p].y]&&y!=fa){
        getsz(y,x);
        sz[x]+=sz[y];
    }
}

void getrt(int x,int fa){
    int f,p,y;
    f=size-sz[x];
    for(p=g[x];p;p=e[p].next)
    if(!vis[y=e[p].y]&&y!=fa){
        getrt(y,x);
        f=max(f,sz[y]);
    }
    if(f<mi) mi=f,rt=x;
}

void find(int dep,int supply){
    int l=1,r=size,mid;
    while(l<r){
        mid=(l+r)/2 +1;
        if(a[mid]<=supply) l=mid;
        else r=mid-1;
    }
    ans=max(ans,l+dep);
}

void getup(int x,int fa,int dep,int nw,int mn){
    if(mn>=0){
        find(dep,nw);
        mn=0;
    }
    int p,y;
    for(p=g[x];p;p=e[p].next)
    if(!vis[y=e[p].y]&&y!=fa)
        getup(y,x,dep+1,nw+val[y]-e[p].d, mn+val[y]-e[p].d);
}

void getdw(int x,int fa,int dep,int nw,int mn){
    mn=min(mn,nw);
    a[dep]=min(a[dep],-min(mn,0));
    int p,y;
    for(p=g[x];p;p=e[p].next)
    if(!vis[y=e[p].y]&&y!=fa)
        getdw(y,x,dep+1,nw+val[x]-e[p].d, mn);
}

void calc(int x){
    int i,j,p,y,d;
    
    getsz(x,0);
    
    tq=0;
    for(p=g[x];p;p=e[p].next)
    if(!vis[y=e[p].y]) que[++tq]=node(e[p].y,e[p].d);
    
    for(i=1;i<=sz[x]+1;i++) a[i]=INF;
    a[1]=0;
    for(i=1;i<=tq;i++){
        y=que[i].y; d=que[i].d;
        getup(y,0,1,val[y]-d,val[y]-d);
        getdw(y,0,2,val[x]-d,INF);
        for(j=sz[y]+1;j>0;j--) a[j]=min(a[j],a[j+1]);
    }
    
    for(i=1;i<=sz[x]+1;i++) a[i]=INF;
    a[1]=0;
    for(i=tq;i>=1;i--){
        y=que[i].y; d=que[i].d;
        getup(y,0,1,val[y]-d,val[y]-d);
        getdw(y,0,2,val[x]-d,INF);
        for(j=sz[y]+1;j>0;j--) a[j]=min(a[j],a[j+1]);
    }
}

void work(int fr){
    getsz(fr,0);
    mi=size=sz[fr];
    getrt(fr,0);
    int x=rt,p,y;
    vis[x]=1;
    calc(x);
    for(p=g[x];p;p=e[p].next)
    if(!vis[y=e[p].y]){
        work(y);
    }
}

int main(){
    int i,x,y,z;
    n=rd();
    for(i=1;i<=n;i++) val[i]=rd();
    for(i=1;i<n;i++){
        x=rd(),y=rd(),z=rd();
        addedge(x,y,z);
        addedge(y,x,z);
    }
    work(1);
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/acha/p/6360445.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值