[dp] [dijsktra] [ABC261Ex] Game on Graph
posted on 2024-04-25 02:52:41 | under | source
令 \(f_{u,0/1}\) 表示节点 \(u\) 出发且 \(\rm Takahashi\) 或 \(\rm Aoki\) 先手时的得分。
转移是显然的,\(\forall v,u\to v\):
\(f_{u,0} = \min(f_{v,1}+w(u,v))\)。
\(f_{u,1}= \max(f_{v,0}+w(u,v))\)。
如果是 \(\rm DAG\) 拓扑排序就行了,但本题有环,怎么办?
直观的想法是跑 \(\rm dijsktra\),不过是错的,无法保证 \(f_{u,1}\) 取到正确的值。
那么以 \(f_{u,0}\) 为主元(即每次取最小值更新),将 \(f_{u,1}\) 视为辅助转移的。\(\rm dijsktra\) 保证了 \(f_{u,0}\) 能取到正确值。那么对于 \(f_{u,1}\),令 \(\forall u\to v\) 的 \(f_{v,0}\) 都取到正确值后再转移即可。
整体上看,就相当于通过正确的值转移得到其它正确的值(废话)。
如果到最后 \(f_{v,0}\) 尚未取到正确值,则说明 \(f_{v,0}\) 是极大值,依此类推能得到 \(f_{u,1}\) 是极大值这样的。所以若 \(f_{s,0}=inf\) 则最后答案就是 INFINITY。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, inf = 1e18;
int n, m, s, f[N][2], u, v, w, chu[N];
bool vis[N][2];
vector<pair<int, int> > to[N];
struct node{int u, f, id;};
inline bool operator < (node A, node B) {return A.f > B.f;}
priority_queue<node> q;
signed main(){
cin >> n >> m >> s;
for(int i = 1; i <= m; ++i) scanf("%lld%lld%lld", &u, &v, &w), to[v].push_back({u, w}), ++chu[u];
for(int i = 1; i <= n; ++i){
f[i][0] = inf, f[i][1] = -inf;
if(!chu[i]) q.push({i, f[i][0] = 0, 0}), q.push({i, f[i][1] = 0, 1});
}
while(!q.empty()){
node P = q.top(); q.pop();
int u = P.u, id = P.id;
if(vis[u][id]) continue;
vis[u][id] = true;
for(auto E : to[u]){
int v = E.first, w = E.second;
if(!id){
--chu[v], f[v][1] = max(f[v][1], f[u][0] + w);
if(!chu[v] && !vis[v][1]) q.push({v, f[v][1], 1});
}
else{
f[v][0] = min(f[v][0], f[u][1] + w);
if(!vis[v][0]) q.push({v, f[v][0], 0});
}
}
}
if(f[s][0] == inf) cout << "INFINITY";
else cout << f[s][0];
return 0;
}

浙公网安备 33010602011771号