题意:n个点,可以在点和点之间互相跳,给出在每个点跳一次的花费,以及他可以跳到的范围,求从第一个点跳到所有点的最小花费。
很像单源最短路,但直接暴力肯定不行,因为是从第一个点开始跳,发现已经跳到的点一定是最小花费,后续不需要在考虑这个点,因此可以想办法优化掉,第一个想到的是双向链表,但发现维护起来并不合适,后来又想到直接用并查集就可以了,用并查集求每个点右边的第一个没去掉的点,也就是可以入队的点。去掉当前这个点,判断一下他左边挨着的点有没有去掉,去掉的话更新一下左边点的并查集就可以了。
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 2e5 + 10;
int l[2][maxn], r[2][maxn], v[maxn], f[maxn], book[maxn], n;
struct node{
int now;
long long cost;
bool operator < (node u)const{
return cost > u.cost;
}
};
long long ans[maxn];
int find_(int x){
if(f[x] == x)
return x;
return f[x] = find_(f[x]);
}
int merge(int u, int v){
if(u == 0)
return 0;
int t1 = find_(u);
int t2 = find_(v);
if(t1 != t2){
f[t1] = t2;
return 1;
}
return 0;
}
void delete_(int x){
if(ans[x - 1] != -1)
merge(x - 1, x);
return ;
}
void solve(){
for(int i = 1; i <= n; i++){
f[i] = i, ans[i] = -1;
}
priority_queue<node>Q;
node be;
be.now = 1, be.cost = v[1], ans[1] = 0;
Q.push(be);
while(!Q.empty()){
node pe = Q.top(); Q.pop();
// cout << endl;
// cout << pe.now << " " << pe.cost << endl;
for(int i = 0; i < 2; i++){
int now = l[i][pe.now];
while(now <= r[i][pe.now]){
// cout << now << endl;
if(ans[now] == -1){
ans[now] = pe.cost;
delete_(now);
node be;
be.now = now;
be.cost = pe.cost + v[now];
Q.push(be);
now++;
}
else{
now = find_(now) + 1;
}
}
}
}
return ;
}
int main(){
int T, u;
cin >> T;
while(T--){
cin >> n;
for(int i = 1; i <= n; i++){
scanf("%d", &u);
r[0][i] = max(i - u, 0);
l[1][i] = min(i + u, n + 1);
}
for(int i = 1; i <= n; i++){
scanf("%d", &u);
l[0][i] = max(i - u, 1);
r[1][i] = min(i + u, n);
}
for(int i = 1; i <= n; i++){
scanf("%d", &v[i]);
}
solve();
for(int i = 1; i <= n; i++){
printf("%lld", ans[i]);
if(i == n)
cout << endl;
else
printf(" ");
}
}
return 0;
}