P7408 [JOI 2021 Final] 地牢 3 题解

Description

有一个 N+1N+1 层的地牢,在地牢里有 MM 个玩家。地牢的每层从入口开始,用 11N+1N+1 的整数编号。玩家从 11MM 标号。

玩家使用能量从一层移动到下一层。玩家从第 i (1iN)i\ (1\le i\le N) 层移动到第 i+1i+1 层所用的能量为 AiA_i。因为这是一个单向通行的地牢,所以玩家只能从第 ii 层移动到第 i+1i+1 层,并且要保证 1iN1\le i\le N

在第 11 到第 NN 层的每层(包括第 11 和第 NN 层)都有治疗泉。在第 ii 层的治疗泉处,玩家可以花 BiB_i 金币,使自己的能量增加 11。只要玩家有足够的金币,他就可以多次使用治疗泉。但是,每个玩家都有最大能量,使用治疗泉的话也不能使自己的能量超过最大值。

现在玩家 j (1jM)j\ (1\le j\le M) 在第 SjS_j 层。他目前的能量为 00。他的最大能量是 UjU_j。他想要到第 TjT_j 层。在路上他的能量不能小于 00。他需要多少金币呢?

给定这个地牢和玩家的信息,写一个程序计算对于每个玩家,是否可以在移动过程中能量不小于 00 的情况下到达目的地。如果可以的话,计算他最少需要多少金币才能达成目标。

对于所有数据,满足:

1N,M2×1051\le N,M\le 2\times 10^5
1Ai,Bi2×105 (1iN)1\le A_i,B_i\le 2\times 10^5\ (1\le i\le N)
1Sj<TjN+1 (1jM)1\le S_j<T_j\le N+1\ (1\le j\le M)
1Uj108 (1jM)1\le U_j\le 10^8\ (1\le j\le M)

Solution

首先考虑对题意进行转化,设 ci=j<iajc_i=\sum_{j<i}{a_j},将走地牢转化为在数轴上从 csc_s 走到 ctc_t,而使用治疗泉可以看作是在 [ci,ci+U)[c_i,c_i+U) 这个区间里用 bib_i 的代价选择一个位置激活,问将 [cs,ct)[c_s,c_t) 里所有的位置都激活的最小代价。

容易发现一个位置 pp 的代价是 [pU+1,p][p-U+1,p] 区间内关键点权值的最小值。

考虑对于每个关键点 ii 算出其控制的区间,设 prepreii 之前第一个代价小于 bib_i 的位置,nxtnxtii 之后第一个代价小于 bib_i 的位置。

那么一个位置 ppii 控制当且仅当 pUcpre,pU<ci,p<cnxtp-U\geq c_{pre},p-U<c_i,p<c_{nxt},整理一下为:max{ci,cpre+U}p<min{ci+U,cnxt}\max\{c_i,c_{pre}+U\}\leq p<\min\{c_i+U,c_{nxt}\}

于是关键点 ii 对答案的贡献为 bi×(min{ci+U,cnxt}max{ci,cpre+U})b_i\times\left(\min\{c_i+U,c_{nxt}\}-\max\{c_i,c_{pre}+U\}\right),这是个关于 UU 的分段一次函数,可以用动态开点线段树维护每个 UU 的答案。

对于终点全是 n+1n+1 的子任务直接将询问离线下来倒着枚举关键点,同时维护一个代价从栈顶到栈底递增的单调栈来求出每个点的 preprenxtnxt

每次加入一个点 ii 时,将所有 prepreii 的位置 jj 之前的贡献去掉,再加上 (j,prej,nxtj)(j,pre_j,nxt_j) 的贡献,然后加入 (i,0,nxti)(i,0,nxt_i) 的贡献表示当 s=is=iii 找不到 prepreii 对答案的贡献。

如果终点不固定,则找到询问为 (s,n+1)(s,n+1) 时控制 ctc_t 的关键点 kk,容易发现 ctU<ckctc_t-U<c_k\leq c_t,那么 (s,n+1)(s,n+1)(k,n+1)(k,n+1) 两组询问在 [ct,cn+1)[c_t,c_{n+1}) 的贡献是相等的,并且 (k,n+1)(k,n+1)[ck,ct)[c_k,c_t) 的贡献为 bk(ctck)b_k\cdot(c_t-c_k),所以 ans(s,t)=ans(s,n+1)ans(k,n+1)+bk(ctck)ans(s,t)=ans(s,n+1)-ans(k,n+1)+b_k\cdot(c_t-c_k),就转化为终点固定的情况了。

时间复杂度:O((n+m)logV)O((n+m)\log V)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 2e5 + 5;

int n, m, rt, sgt_cnt;
int a[kMaxN], b[kMaxN], c[kMaxN], ans[kMaxN];
int lg[kMaxN], sta[kMaxN][20], stb[kMaxN][20];
int ls[kMaxN * 60], rs[kMaxN * 60], sum[kMaxN * 60], tag[kMaxN * 60];
std::vector<std::tuple<int, int, int>> qq[kMaxN];

int get(int x, int y) {
return b[x] < b[y] ? x : y;
}

void st_prework() {
lg[0] = -1;
for (int i = 1; i <= n + 1; ++i) {
sta[i][0] = a[i];
stb[i][0] = i;
lg[i] = lg[i >> 1] + 1;
}
for (int i = 1; i <= lg[n + 1]; ++i) {
for (int j = 1; j <= n + 1 - (1 << i) + 1; ++j) {
sta[j][i] = std::max(sta[j][i - 1], sta[j + (1 << (i - 1))][i - 1]);
stb[j][i] = get(stb[j][i - 1], stb[j + (1 << (i - 1))][i - 1]);
}
}
}

int querya(int l, int r) {
int k = lg[r - l + 1];
return std::max(sta[l][k], sta[r - (1 << k) + 1][k]);
}

int queryb(int l, int r) {
int k = lg[r - l + 1];
return get(stb[l][k], stb[r - (1 << k) + 1][k]);
}

void prework() {
st_prework();
}

void update(int &x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
if (!x) x = ++sgt_cnt;
sum[x] += 1ll * v * (std::min(r, qr) - std::max(l, ql) + 1);
if (l >= ql && r <= qr) return void(tag[x] += v);
int mid = (l + r) >> 1;
update(ls[x], l, mid, ql, qr, v), update(rs[x], mid + 1, r, ql, qr, v);
}

int query(int x, int l, int r, int ql, int qr) {
if (!x || l > qr || r < ql) return 0;
else if (l >= ql && r <= qr) return sum[x];
int mid = (l + r) >> 1;
return query(ls[x], l, mid, ql, qr) + query(rs[x], mid + 1, r, ql, qr) + 1ll * tag[x] * (std::min(r, qr) - std::max(l, ql) + 1);
}

int res[kMaxN];

void upd(int l, int r, int k, int b, int v) {
l = std::max<int>(l, 0), r = std::min<int>(r, 1e8);
if (l > r || !k && !b) return;
// std::cerr << l << ' ' << r << ' ' << k << ' ' << b << ' ' << v << '\n';
update(rt, 0, 1e8, l, l, (k * l + b) * v);
update(rt, 0, 1e8, l + 1, r, k * v);
update(rt, 0, 1e8, r + 1, r + 1, -(k * r + b) * v);
// for (int i = l; i <= r; ++i) res[i] += v * (k * i + b);
}

void work(int x, int pre, int nxt, int v) {
assert(x > pre && x < nxt);
std::vector<int> vec = {0, c[nxt] - c[x]};
if (pre) vec.emplace_back(c[nxt] - c[pre]), vec.emplace_back(c[x] - c[pre]);
else vec.emplace_back(1e8 + 1);
std::sort(vec.begin(), vec.end());
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
for (int i = 0; i + 1 < (int)vec.size(); ++i) {
int l = vec[i], r = vec[i + 1] - 1, k = 0, b = 0;
if (c[x] + l <= c[nxt] && c[x] + r <= c[nxt]) ++k, b += c[x];
else b += c[nxt];
if (c[x] >= c[pre] + l && c[x] >= c[pre] + r) b -= c[x];
else --k, b -= c[pre];
upd(l, r, k, b, v * ::b[x]);
}
// for (int u = 0; u <= 1e3; ++u) {
// if (std::max(c[i], c[pre] + u) < std::min(c[i] + u, c[nxt]))
// res[u] += v * b[i] * (std::min(c[i] + u, c[nxt]) - std::max(c[i], c[pre] + u));
// }
}

void getans() {
static int top, stk[kMaxN], nxt[kMaxN];
stk[top = 1] = n + 1;
c[0] = -1e9;
for (int i = n; i; --i) {
for (; top && b[stk[top]] >= b[i]; --top) {
work(stk[top], 0, nxt[stk[top]], -1);
work(stk[top], i, nxt[stk[top]], 1);
}
nxt[i] = stk[top];
stk[++top] = i;
work(i, 0, nxt[i], 1);
for (auto [id, u, v] : qq[i]) ans[id] += v * query(rt, 0, 1e8, 0, u);
// for (auto [id, u, v] : qq[i]) ans[id] += v * res[u];
}
}

void dickdreamer() {
std::cin >> n >> m;
for (int i = 1; i <= n; ++i) {
std::cin >> a[i];
c[i + 1] = c[i] + a[i];
}
for (int i = 1; i <= n; ++i)
std::cin >> b[i];
prework();
for (int i = 1; i <= m; ++i) {
int s, t, u;
std::cin >> s >> t >> u;
if (querya(s, t - 1) > u) {
ans[i] = -1;
continue;
}
int p = std::max(s, std::upper_bound(c + 1, c + 2 + n, c[t] - u) - c), k = queryb(p, t);
ans[i] = b[k] * (c[t] - c[k]);
qq[s].emplace_back(i, u, 1), qq[k].emplace_back(i, u, -1);
}
getans();
for (int i = 1; i <= m; ++i) std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P7408 [JOI 2021 Final] 地牢 3 题解
https://sobaliuziao.github.io/2024/10/30/post/2bcb5ecc.html
作者
Egg_laying_master
发布于
2024年10月30日
许可协议