[BZOJ1922][Sdoi2010]大陆争霸(Dijkstra)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=1922

Solution

定义状态:
f[u] f [ u ] 表示到达并进入城市 u u 的最短时间。

f[u]=max(min<v,u>{f[v]+len<v,u>},maxvuf[v])

当然,这个方程是假的,因为自己会间接地转移到自己。
看到既有 max max 又有 min min ,考虑用最短路解决。
这里选择 Dijkstra
不过,我们新的定义是:
f[u] f [ u ] 表示到达城市 u u 的最短时间。
g[u] 表示摧毁城市 u u 结界的最短时间。
算法流程:
(1)每次选出一个没有被保护且 max(f,g) 最小的城市 u u 进行扩展。
(2)对于每条边 <u,v> ,用 max(f[u],g[u])+len<u,v> max ( f [ u ] , g [ u ] ) + l e n < u , v > 更新 f[v] f [ v ]
(3)对于每个被 u u 发生器维持结界的城市 v ,用 max(f[u],g[u]) max ( f [ u ] , g [ u ] ) 更新 g[v] g [ v ] 。(注:这里的更新是取 max max
(4) u u 的发生器被摧毁。
(5)以上循环 n 次。
最后答案 max(f[n],g[n]) max ( f [ n ] , g [ n ] )

Code

// luogu-judger-enable-o2
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define Edge2(u) for (int e = adj2[u], v = go2[e]; e; e = nxt2[e], v = go2[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll; const int N = 3005, M = 7e4 + 5, L = 9e6 + 5;
int n, m, ecnt, nxt[M], adj[N], go[M], val[M], ecnt2, nxt2[L], adj2[N],
go2[L], cnt[N]; ll f1[N], f2[N]; bool mark[N];
void add_edge(int u, int v, int w) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; val[ecnt] = w;
}
void add_edge2(int u, int v) {
    nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v;
}
int main() {
    int i, j, x, y, z; n = read(); m = read();
    For (i, 1, m) x = read(), y = read(), z = read(), add_edge(x, y, z);
    For (i, 1, n) {
        cnt[i] = read(); For (j, 1, cnt[i]) x = read(), add_edge2(x, i);
    }
    For (i, 2, n) f1[i] = 1ll << 62; int T = n; while (T--) {
        ll mx = 1ll << 62; int p = -1; For (i, 1, n) if (!mark[i] && !cnt[i]
            && max(f1[i], f2[i]) < mx) mx = max(f1[i], f2[i]), p = i;
        mark[p] = 1; Edge(p) if (!mark[v]) f1[v] = min(f1[v],
            max(f1[p], f2[p]) + val[e]);
        Edge2(p) if (!mark[v]) f2[v] = max(f2[v], max(f1[p], f2[p])), cnt[v]--;
    }
    cout << max(f1[n], f2[n]) << endl; return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值