题意:
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5361
有n个点,每个点有一个可以到达点的范围[l,r],传送一次距离为c,问从点1到所有点的最短距离是多少。
思路:
注意到从点1开始,如果利用优先队列选出将要到达的最近的点,那么每个点最多只需要更新一次,如果用传统的最短路,会浪费大量的时间在扫描那些已经更新过的点上,因此需要利用并查集优化,每次更新一个点后将其与后一个点的集合合并,这样可以快速找到从点u开始向右第一个没有更新的点v;
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 2e5 + 10;
struct node {
int u;
LL d;
bool operator < (const node &rhs) const {
return d > rhs.d;
}
};
int pa[MAXN];
int Find(int x) {
return x == pa[x] ? x : pa[x] = Find(pa[x]);
}
void Uion(int x, int y) {
int px = Find(x), py = Find(y);
if (px != py) {
pa[px] = py;
}
}
int l[MAXN], r[MAXN];
LL w[MAXN], dis[MAXN];
int main() {
//freopen("in.txt", "r", stdin);
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &l[i]);
for (int i = 1; i <= n; i++) scanf("%d", &r[i]);
for (int i = 1; i <= n; i++) scanf("%I64d", &w[i]);
for (int i = 1; i <= n; i++) pa[i] = i, dis[i] = INF;
priority_queue <node> que;
dis[1] = 0;
que.push((node){1, w[1]});
while (!que.empty()) {
node now = que.top(); que.pop();
int u = now.u;
for (int i = -1; i <= 1; i += 2) {
int L = u + l[u] * i;
int R = u + r[u] * i;
if (L > R) swap(L, R);
L = max(1, L);
R = min(n, R);
for (int v = L; v <= R; v++) {
v = Find(v);
if (v > R) break;
if (dis[v] > dis[u] + w[u]) {
dis[v] = dis[u] + w[u];
que.push((node){v, dis[v] + w[v]});
}
if (v + 1 <= n) Uion(v, v + 1);
}
}
}
for (int i = 1; i <= n; i++) {
printf("%I64d%c", dis[i] >= INF ? -1 : dis[i], i == n ? '\n' : ' ');
}
}
return 0;
}