【Vijos 1880 ファーラの力】【思维题】

(每次到达一个点先补充人的光压,再走) 就等价于 (先走到 n 号点,最后再补充人的光压)。
因为我们求的是到达第 N 个光柱光压最大所需要的最少时间,所以还要加一个 E[n] 补到最大值。

对于 40% 的数据, X=0 的数据,答案就是 dis[n]2+E[n]

考虑 X0 的情况。
f[u] 表示人到点 u 的最大光压(因为我们是在最后补充人的光压,所以 f[] 是一直递减的)。
如果 f[n] 为负,我们要先把它补到 0 再补到最大值,即 先走到 n 要用的光压 + 补充人的光压(即等待的时间) + 补到最大值,(X - f[n]) + (-f[n]) + (E[n])
如果 f[n] 为正,说明从未到过 0 ,那么我们只要计算人走到 n 要用的光压再补到最大值即可,(X - f[n]) + (E[n] - f[n])
所以我们无需分正负讨论,答案都为 E]n]+X2f[n]

如何计算 f[n] 呢,即计算人的最大光压,显然如果一直走最短路即是最大光压。
Dijstra 注意是大根堆,因为我们是一直减的,这样才能保证正确性。
注意开 ll

#include 
#define ll long long

using namespace std;
const int N = 1e5 + 5;
const ll inf = 1e16;

struct Edge {
    int to, next; ll w; 
}e[N << 3];

int cnt = 0;
int head[N], vis[N];
void add(int u, int v, ll w) {
    e[++ cnt].to = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt;
}

struct Point {  
    int id; ll val;
    Point(int id, ll val) : id(id), val(val) {}
    bool operator < (const Point& a) const {
        return val < a.val;
    }
};

priority_queue q;

int n, m;
ll X;
ll f[N], E[N];
void dijstra() {
    for (int i = 1; i <= n; i ++) f[i] = inf;
    q.push(Point(1, X));
    while (!q.empty()) {
        Point u = q.top(); q.pop();

        if (vis[u.id]) continue;
        vis[u.id] = 1;
        f[u.id] = u.val;

        for (int i = head[u.id]; i; i = e[i].next) {
            int v = e[i].to;
            q.push(Point(v, min(E[v], f[u.id] - e[i].w)));  
        }
    }
}

int main() {
    memset(vis, 0, sizeof(vis));
    scanf("%d%d%lld", &n, &m, &X);
    for (int i = 1; i <= n; i ++) scanf("%lld", &E[i]);
    for (int i = 1; i <= m; i ++) {
        int u, v; ll w;
        scanf("%d%d%lld", &u, &v, &w);
        if (w <= E[u]) add(u, v, w);
        if (w <= E[v]) add(v, u, w);    
    }
    dijstra();
    if (vis[n]) printf("%lld\n", E[n] + X - 2 * f[n]);
    else printf("-1\n");
    return 0;   
}

你可能感兴趣的:(-----,思维题,-----)