J Journey among Railway Stations
题意
一条线上存在n
个站点,每个站点能够进站的时间为
[
u
i
,
r
i
]
[u_i,r_i]
[ui,ri],并且
i
i
i站点到
i
+
1
i+1
i+1站点需要花费的时间为
c
o
s
t
i
cost_i
costi。
如果列车在 u i u_i ui之前到达,需要等一段时间;如果列车在 v i v_i vi之后到达,则不能通过站点 i i i。
有3中操作:
- 给定两个值
i
、j
:求列车能否从 i i i站点到 j j j站点。 - 给定两个值
i
、w
:将站点 i i i到站点 i + 1 i+1 i+1之间的花费 c o s t i cost_i costi修改为 w w w。 - 给定三个值
i
、p
、q
:将站点 i i i能够进站的时间修改为 [ p , q ] [p,q] [p,q]。
思路
根据题意,可以看出当进站时间小于 u i u_i ui时,需要等到 u i u_i ui;当进站时间超过 v i v_i vi时,不合法。(如下图 f ( x ) f(x) f(x))
定义几个参数:
- 最迟到达当前节点的时间:latest_in
- 从当前节点出来最早的时间:earliest_out
- 区间内的cost和:sum_cost
注意:每个节点是这样一个函数 f ( x ) f(x) f(x),两个相邻的节点合并之后依旧满足这个类型的函数,所以可以用线段树来维护这样一个区间信息。
以下面数据为例说明:
5
1 2 3 4 5
3 4 5 6 7
1 1 1 1
将站点1 和 站点2进行合并:站点1的进站时间为 [ 1 , 3 ] [1,3] [1,3],站点2的进站时间为 [ 2 , 3 ] [2,3] [2,3]。
合并后:
这个是核心
-
latest_in = min(l.latest_in, r.latest_in - l.sum_cost):
-
如果慢于l.max_t会赶不上左边的进站时间;
-
如果慢于r.max_t-l.sum_c会赶不上右边的进站时间。
-
-
earliest_out = max(l.earliest_out + r.sum_cost, r.earliest_out)
- l.earliest_out + r.sum_cost 左区间出来后最快能到右边的时间;
- r.earliest_out 右区间最快出来的时间。
-
sum_cost = l.sum_cost + r.sum_cost
- 两个区间的 cost 相加。
AC的代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10, inf = 0x3f3f3f3f;
int n, u[N], v[N], cost[N];
struct Node {
int latest_in, earliest_out, sum_cost;
}node[N<<2];
Node operator +(Node l, Node r) {
if (l.earliest_out > r.latest_in) {
// 左区间最快的出来时间也赶不上右边的最迟到达时间
// 无法从l到达r
return { -1, inf, inf };
}
return {
min(l.latest_in, r.latest_in - l.sum_cost),
max(l.earliest_out + r.sum_cost, r.earliest_out),
l.sum_cost + r.sum_cost
};
}
#define cl (k*2)
#define cr (k*2+1)
#define mid ((l+r)/2)
void pushup(int k) {
node[k] = node[cl] + node[cr];
}
// 很妙的写法,初始化+单点修改
void init(int x) {
int k = 1, l = 1, r = n;
while (l != r) {
if (x <= mid) {
k = cl; r = mid;
}
else {
k = cr; l = mid + 1;
}
}
node[k] = { v[x], min(u[x] + cost[x], inf), cost[x] };
while (k >>= 1)
pushup(k);
}
int ql, qr;
Node now;
// 向下递归
void dfs(int k, int l, int r) {
if (ql <= l && qr >= r) {
now = now + node[k];
return;
}
if (ql <= mid)
dfs(cl, l, mid);
if (qr > mid)
dfs(cr, mid + 1, r);
}
bool query(int l, int r) {
if (l == r) return true;
ql = l;
qr = r - 1;
now = { inf, 0, 0 };
dfs(1, 1, n);
return now.earliest_out <= v[r];
}
int main() {
int t, Q;
cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) cin >> u[i];
for (int i = 1; i <= n; i++) cin >> v[i];
for (int i = 1; i < n; i++) cin >> cost[i];
for (int i = 1; i <= n; i++) init(i);
cin >> Q;
while (Q--) {
int ty;
cin >> ty;
if (ty == 0) {
int l, r;
cin >> l >> r;
if (query(l, r)) cout << "Yes\n";
else cout << "No\n";
}
else
if (ty == 1) {
int i, w;
cin >> i >> w;
cost[i] = w;
init(i);
}
else {
int i;
cin >> i >> u[i] >> v[i];
init(i);
}
}
}
return 0;
}