Wannafly挑戰賽2 B-Travel(抽40個點跑最短路)
阿新 • • 發佈:2019-02-17
發現這道題真的挺難寫的。。。如果單純是個環,我們直接用字首和就能求出兩點之間的距離,現在額外添加了一些邊。我們分情況討論。
1.只走環上的點,那麼直接字首和相減就能得到答案。
2.走了傳送門。從起點出發,那麼可能先走a傳送門,再走b傳送門……最終到達終點。
我們最後只需要在1得到的答案和2得到的答案中取min即可。
1.很好解決,先處理出字首和 和所有邊的總和length,然後兩個點的字首和相減取絕對值可以得到某個方向的距離ans,再用length-ans可以得到另外一個方向的距離。取min。
2.我們可以發現這種走法可以理解為從任意一個傳送門開始走,到達某個點,其中起點s到終點e就是該傳送門到起點s的距離+該傳送門到終點e的距離
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define SIZE(x) (int)(x).size() typedef long long LL; typedef pair<LL, int> pii; const int maxn = 60000 + 5; const LL INF = 1e18; int n, m; LL G[45][maxn], sum[maxn], a[maxn], length; vector <pii> edge[maxn]; vector <int> ver; void dijkstra(int s) { priority_queue <pii, vector<pii>, greater<pii> > q; for(int i = 1; i <= n; i++) G[s][i] = INF; G[s][ver[s]] = 0; q.push(pii(G[s][ver[s]], ver[s])); while(!q.empty()) { pii tmp = q.top(); q.pop(); if(G[s][tmp.second] < tmp.first) continue; int u = tmp.second; for(int i = 0; i < SIZE(edge[u]); i++) { int v = edge[u][i].second; LL val = edge[u][i].first; if(G[s][v] > G[s][u] + val) { G[s][v] = G[s][u] + val; q.push(pii(G[s][v], v)); } } } } int main() { #ifndef ONLINE_JUDGE // freopen("in.txt", "r", stdin); // freopen("out.txt", "w", stdout); #endif cin >> n >> m; for(int i = 1; i <= n; i++) { cin >> a[i]; length += a[i]; sum[i] = a[i] + sum[i - 1]; edge[i].push_back(pii(a[i], (i % n) + 1)); edge[(i % n) + 1].push_back(pii(a[i], i)); } while(m--) { int u, v, val; cin >> u >> v >> val; edge[u].push_back(pii(val, v)); edge[v].push_back(pii(val, u)); ver.push_back(u); ver.push_back(v); } int p = unique(ver.begin(), ver.end()) - ver.begin(); for(int i = 0; i < p; i++) { dijkstra(i); } int k; cin >> k; while(k--) { int u, v; cin >> u >> v; LL ans = abs(sum[v - 1] - sum[u - 1]); ans = min(ans, length - ans); for(int i = 0; i < p; i++) { ans = min(ans, G[i][u] + G[i][v]); } cout << ans << endl; } return 0; }