题目
题目概要
在位置
i
i
i 时,可以走到
y
(
L
i
⩽
∣
y
−
i
∣
⩽
R
i
)
y\;(L_i\leqslant|y-i|\leqslant R_i)
y(Li⩽∣y−i∣⩽Ri),代价是
c
i
c_i
ci,求从
1
1
1 出发到每个点的最短路。
数据范围与提示
0
⩽
L
i
⩽
R
i
⩽
n
⩽
2
×
1
0
5
0\leqslant L_i\leqslant R_i\leqslant n\leqslant 2\times 10^5
0⩽Li⩽Ri⩽n⩽2×105 且
n
≠
0
n\ne 0
n=0,显然
1
⩽
c
i
⩽
1
0
9
1\leqslant c_i\leqslant 10^9
1⩽ci⩽109 。
思路
简单的方法:线段树优化建图。
如果不那么做呢?考虑像这种题一样,有一个类似线段树懒标记的操作,一点点将标记释放。那么我们的标记相当于:对某个区间内所有的 d i s dis dis 都松弛一下。
当然,其实对 d i s dis dis 的松弛只有这一种。所以我们只考虑这种标记的顺序。显然是先用 t a g tag tag 小的标记来松弛。不过又是具体怎么松弛的呢?显然就是直接找到区间内某个 d i s ⩾ t a g dis\geqslant tag dis⩾tag 的点,然后更新。
怎么找
d
i
s
dis
dis 较大的点呢?带修主席树 并查集。如果我们要用并查集,我们得考虑取出所有
d
i
s
dis
dis 较小的点。所以我们有了这样的思路:两个堆,分别维护
t
a
g
tag
tag 和
d
i
s
dis
dis,每次比较堆顶,
d
i
s
dis
dis 小就取出来更改并查集,顺便把它可以提供的
t
a
g
tag
tag 塞进堆里;
t
a
g
tag
tag 小就取出来进行修改操作。
直接这么做当然可以了。不过代码还可以更简单一点。考虑 d i s dis dis 那个堆的作用:修改并查集,与提供 t a g tag tag 。第二项可以不必在从堆中取出的时候进行,可以在 d i s dis dis 被更新的时候,立刻加入 t a g tag tag,因为 d i s dis dis 只可能被最小的 t a g tag tag 更新一次。
第一项,修改并查集,也不必在从堆中取出的时候进行。我们不需要显式地修改并查集;在 t a g tag tag 寻找的时候,如果找到了一个 d i s ⩽ t a g dis\leqslant tag dis⩽tag,就在并查集里修改一下。
再进一步,每个 d i s dis dis 其实就是之前的某个 t a g tag tag 。考虑到 t a g tag tag 是递增的,只需要在每个 d i s dis dis 被更新的时候,直接修改并查集即可。过程类似于:光的反射,每次找最明亮的光,把所有可行的没使用过的镜子找到,记录光的信息,然后将反射光线纳入考虑范围。
所以代码就简单多了。时间复杂度 O ( n log n ) \mathcal O(n\log n) O(nlogn) 。
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long int_;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int_ x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar((x-x/10*10)^48);
}
inline int ABS(const int &x){
return x < 0 ? -x : x;
}
const int_ infty = (1ll<<60)-1;
const int MaxN = 200005;
int_ dis[MaxN]; int c[MaxN];
struct Node{
int x; Node(int X):x(X){}
bool operator < (const Node &t) const {
return dis[x]+c[x] > dis[t.x]+c[t.x];
}
};
priority_queue<Node> pq;
struct UFS{
int fa[MaxN];
void init(int n){
rep(i,0,n+1) fa[i] = i;
}
inline int find(int a){
if(fa[a] != a)
fa[a] = find(fa[a]);
return fa[a];
}
void merge(int a,int b){
fa[find(a)] = find(b);
}
};
UFS ufs;
int l[MaxN], r[MaxN];
void dijkstra(int n){
rep(i,1,n) dis[i] = infty;
dis[1] = 0; pq.push(Node(1));
ufs.init(n); ufs.merge(1,2);
while(!pq.empty()){
int x = pq.top().x; pq.pop();
for(int i=-1; i<=1; i+=2){
int L = x+i*l[x], R = x+i*r[x];
if(i == -1) L ^= R ^= L ^= R;
if(L > n) continue; // bad
int p = ufs.find(max(1,L));
while(p != n+1 && p <= R){
dis[p] = dis[x]+c[x];
pq.push(Node(p));
ufs.merge(p,p+1);
p = ufs.find(p);
}
}
}
}
int main(){
for(int T=readint(); T; --T){
int n = readint();
rep(i,1,n) l[i] = readint();
rep(i,1,n) r[i] = readint();
rep(i,1,n) c[i] = readint();
dijkstra(n), putchar('0');
rep(i,2,n){
if(dis[i] == infty)
dis[i] = -1;
putchar(' ');
writeint(dis[i]);
}
putchar('\n');
}
return 0;
}