「题解」Codeforces 360E Levko and Game
现在明确我们的目的:能赢就选择赢的方案,否则尝试平局;并不是求和对方差值更大的方案。
考虑一个赢/平局的方案,考虑每一条边,没有被任何一个经过,那么调整到任何值都是无所谓的;如果仅被 \(s_1\) 经过,调整到 \(l\) 是更优的;如果仅被 \(s_2\) 经过,调整到 \(r\) 是更优的;如果同时被 \(s_1,s_2\) 经过,那么一定是一个平局的方案,把这条边改得更小还会使 \(s_1,s_2\) 经过这里,如果把这条边改得更大反而可能让 \(s_1,s_2\) 走其他路从而变成一个 \(s_1\) 赢的方案,如果不存在让 \(s_1\) 赢的方案再把它改成 \(l\) ,也就是尽可能地让 \(s_1,s_2\) 一起走到这里,促成平局。
综上所述,一定存在一个最优方案使得每条边的权值要不然为 \(l\),要不然为 \(r\)。
考虑如何让 \(s_1\) 赢:对于一条边 \((u,v)\):
-
若 \(dis(s_1,u)\geq dis(s_2,u)\),那么 \(s_1\) 一定不会走这条路,因为走到这条路之后和 \(s_2\) 的最优路线重合,一定不会赢,所以改成 \(r\) 让 \(s_2\) 走这条路的开销更大;
-
若 \(dis(s_1,u)<dis(s_1,u)\),同理,\(s_2\) 走这条路会使 \(s_1\) 赢,那么 \(s_2\) 不会走这条路,改成 \(l\) 让 \(s_1\) 走这条路的开销更小。
现在判断完了 \(s_1\) 能不能赢,如果不能赢就尽可能平局,和上面的思路基本一致:
-
若 \(dis(s_1,u)>dis(s_2,u)\),那么 \(s_1\) 一定不会走这条路,因为走到这条路之后和 \(s_2\) 的最优路线重合,一定不会平局,所以改成 \(r\) 让 \(s_2\) 走这条路的开销更大;
-
若 \(dis(s_1,u)\leq dis(s_1,u)\),同理,\(s_2\) 走这条路会平局或 \(s_1\) 赢 ,那么 \(s_2\) 不会走这条路,改成 \(l\) 让 \(s_1\) 走这条路的开销更小。
先钦点所有的边都是 \(r\),每次跑 Dijkstra 然后看哪些边要改权值就改,没有边权值要改时再判断,易做到 \(\mathcal{O}(km\log n)\).
实际上可以在最短路每次松弛的时候判断当前边的权值是多少,时间复杂度 \(\mathcal{O}(m\log n)\).
\(\mathcal{Code}\)
#include<iostream>
#include<cstdio>
#include<queue>
#define int long long
typedef long long ll;
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T& read(T& r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 200010;
const ll INF = 0x7fffffffffffffff;
int n, m, k;
int s1, s2, t;
ll dis[N];
struct Edge {
int to, val1, val2, id;
bool fl;
Edge(int x = 0, int y = 0, int z = 0, int p = 0) { to = x; val1 = y; val2 = z; id = p; }
};
int ans[N];
std::vector<Edge>vec[N];
std::queue<int>q;
void Dij(int s1, int s2, int fl) {
for(int i = 1; i <= n; ++i) dis[i] = INF;
dis[s1] = 1; dis[s2] = 0;
q.push(s1); q.push(s2);
while(!q.empty()) {
int x = q.front(); q.pop();
for(Edge &e : vec[x]) {
int v = e.to, w = (e.fl = ((dis[x]&1) == fl)) ? e.val1 : e.val2;
if(dis[v] > dis[x] + w) {
dis[v] = dis[x] + w;
q.push(v);
}
}
}
}
signed main() {
read(n); read(m); read(k);
read(s1); read(s2); read(t);
for(int i = 1; i <= m; ++i) {
int u, v, l; read(u); read(v); read(l);
vec[u].push_back(Edge(v, l<<1, l<<1, 0));
}
for(int i = 1; i <= k; ++i) {
int u, v, l, r; read(u); read(v); read(l); read(r);
vec[u].push_back(Edge(v, l<<1, r<<1, i));
}
Dij(s1, s2, 1);
if(dis[t] & 1) {
puts("WIN");
for(int i = 1; i <= n; ++i)
for(Edge e : vec[i])
ans[e.id] = e.fl ? e.val1 : e.val2;
for(int i = 1; i <= k; ++i) printf("%lld ", ans[i]/2);
}
else {
Dij(s2, s1, 0);
if(!(dis[t]&1)) {
puts("DRAW");
for(int i = 1; i <= n; ++i)
for(Edge e : vec[i])
ans[e.id] = e.fl ? e.val1 : e.val2;
for(int i = 1; i <= k; ++i) printf("%lld ", ans[i]/2);
}
else {
puts("LOSE");
}
}
return 0;
}