vijos1880 ファーラの力

窗外斜阳 日暮西山 倚栏眺
想着当年气盛青涩的年少
时光湍急 岁月汹涌 物是人已非
年年我挥汗洒泪赴沙场
Ninian 的魔力可以在结界间传递。
结界中有 N 个光柱,第 i 个光柱的光压范围为 0~Ei 。魔力可以有 M 种传递,从光柱 Ai 传递到光柱 Bi ,花费时间 Ti 。
当魔力从光压为 S 传递并花费了 T 的时间后,就会衰减到光柱上光压为 S-T 处,S-T 不能为负。
Ninian 可以将魔力的光压花费 1 时间增加 1 或减少 1 ,当然魔力的光压不能超过光柱的光压范围,也不能小于 0 。
Ninian 的魔力初始在 1 号光柱,光压为 X 。
问 Ninian 的魔力到达第 N 个光柱且光压最大所需要的最少时间。

输入格式
第一行三个整数 N, M, X 。
接下来的 N 行每行一个整数表示 Ei 。
接下来的 M 行每行三个整数表示 Ai, Bi, Ti 。
输出格式
输出一个整数表示所需的最少时间,如果 Ninian 的魔力无法到达,输出 -1 。

考虑X=0的情况,发现每次走的时候,先充能,然后再走。答案就是最短路*2+H[n].

考虑X!=0的情况,我们发现如果可以走,那直接走就好。如果不能走,等到走完以后到上限最优。
如果走到0了,那么就再也不需要降低光压了。只需要上升光压即可,所以就和X=0的情况一样。
所以我们可以用f【u】表示当前点的最大光压。(走最短路的光压一定是最大的)。
然后答案怎么思考,我们分情况想一下:
当f【n】为负时,显然那个负数的部分是要走路+充能的。所以是X+H【n】-f[n]*2
当f【n】为正时,那个正数部分显然是剩余光压,说明从来都没有走到过0,那么只要计算行走+补满就好了。X-f【n】为行走部分,H【n】-f【n】是补充部分。所以答案也为X+H【n】-f[n]*2.

所以就是走一个最短路,计算答案。注意最短路过程中,为了防止权值越来越小死循环:
SPFA不够大可以continue,迪杰斯特拉可以改成大根堆。

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int MAXN=1e5+5;
const ll INF=1e16;

struct edge{
    int to,next;ll w;
}e[MAXN<<3];

int n,m,x;
ll dis[MAXN],H[MAXN];

struct hnd{
    int u;ll d;
    bool operator<(const hnd&rhs)const{
        return d<rhs.d;
    }
};
int cnt=0,head[MAXN];
inline void add(int u,int v,ll w){e[++cnt]=(edge){v,head[u],w},head[u]=cnt;}

priority_queue<hnd>q;

void dij(){
    for(int i=1;i<=n;i++)dis[i]=-INF;
    q.push((hnd){1,x});
    while(q.size()){
        hnd x=q.top();q.pop();
        int u=x.u;
        if(dis[u]!=-INF)continue;
        dis[u]=x.d;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;ll w=e[i].w;
            q.push((hnd){v,min(H[v],dis[u]-w)});
        }
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&x);
    for(int i=1;i<=n;i++)scanf("%lld",&H[i]);
    for(int i=1;i<=m;i++){
        int u,v;ll w;
        scanf("%d%d%lld",&u,&v,&w);
        if(w<=H[u])add(u,v,w);
        if(w<=H[v])add(v,u,w);
    }
//  for(int i=head[4];i;i=e[i].next)cout<<e[i].to;
    dij();
    if(dis[n]!=-INF)printf("%lld\n",x+H[n]-(dis[n]<<1));
    else printf("-1\n");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值