do_while_true

一言(ヒトコト)

「题解」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;
}
posted @ 2021-08-16 09:48  do_while_true  阅读(33)  评论(0编辑  收藏  举报