发现这道题真的挺难写的。。。如果单纯是个环,我们直接用前缀和就能求出两点之间的距离,现在额外添加了一些边。我们分情况讨论。
1.只走环上的点,那么直接前缀和相减就能得到答案。
2.走了传送门。从起点出发,那么可能先走a传送门,再走b传送门……最终到达终点。
我们最后只需要在1得到的答案和2得到的答案中取min即可。
1.很好解决,先处理出前缀和 和所有边的总和length,然后两个点的前缀和相减取绝对值可以得到某个方向的距离ans,再用length-ans可以得到另外一个方向的距离。取min。
2.我们可以发现这种走法可以理解为从任意一个传送门开始走,到达某个点,其中起点s到终点e就是该传送门到起点s的距离+该传送门到终点e的距离,所以我们跑最短路然后得到一个最短路的网络,我们此时得到了该传送门到其他任何一个点的距离。我们只需要把传送门所在的点都跑一遍最短路,最多也才40个点,就可以得到整个网络两点之间的距离了。然后读入起点s和终点e后,我们直接暴力这40个点,取min(p点到s的距离+p点到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;
}