CF671E Organizing a Race 题解

Description

nn 个点排成一行,第 ii 个点与第 i+1i+1 个点之间的距离为 wiw_i ​个单位。

每个点都有一个加油站,第 ii 个点的加油站可以给你的车加能跑 gig_i ​个单位的油。

若一辆初始没有油的车能从 ll 一路向右开到 rr,也能从 rr 一路向左开到 ll,则称 l,rl,r 之间可以往返。

另外,你有 kk 次将某个 gig_i ​加 11 的机会。请你求出在 l,rl,r 之间可以往返的情况下,rl+1r−l+1 的最大值。

n105n\leq 10^5k,wi,gi109k,w_i,g_i\leq 10^9

Solution

首先考虑对于 [l,r][l,r],什么样的 ggww 能满足条件。容易发现需要满足:

{wlglwl+wl+1gl+gl+1i=lr1wii=lr1gi\begin{cases} w_l&\leq g_l\\ w_l+w_{l+1}&\leq g_l+g_{l+1}\\ &\vdots\\ \sum_{i=l}^{r-1}{w_i}&\leq \sum_{i=l}^{r-1}{g_i} \end{cases}

{wr1grwr1+wr2gr+gr1i=lr1wii=l+1rgi\begin{cases} w_{r-1}&\leq g_r\\ w_{r-1}+w_{r-2}&\leq g_r+g_{r-1}\\ &\vdots\\ \sum_{i=l}^{r-1}{w_i}&\leq \sum_{i=l+1}^{r}{g_i} \end{cases}

ak=i=1k(wi1gi1)a_k=\sum_{i=1}^{k}{(w_{i-1}-g_{i-1})}bk=i=1k(giwi1)b_k=\sum_{i=1}^{k}{(g_i-w_{i-1})},则需要满足 al=maxi=lraia_l=\max_{i=l}^{r}{a_i}br=maxi=lrbib_r=\max_{i=l}^{r}{b_i}

那么让 gig_i 加一,会让 ai+1,ai+2,,ana_{i+1},a_{i+2},\ldots,a_{n} 减一和 bi,bi+1,,bnb_{i},b_{i+1},\ldots,b_{n} 加一。

如果只有 aa 的限制,对于固定的 ll 右边的第一个 ax>ala_x>a_l 的位置 xx,需要满足 [l,x1][l,x-1] 这个区间至少需要操作 axala_x-a_l 次,操作过后对于 xx 右边的第一个位置 yy,同样需要满足 [x,y1][x,y-1] 要操作 ayaxa_y-a_x 次。

这启发我们维护一个单调栈,然后从后往前扫 ll,对于栈中的元素 cic_i,贪心地选择在 gci1g_{c_i-1} 操作 aci1acia_{c_{i-1}}-a_{c_i} 次。

同时通过线段树维护出操作后的 bb 数组,题目转化为:区间加,查询最大的 rr 满足 maxi=lrbibrk\max_{i=l}^{r}{b'_i}-b'_r\leq k'

注意到 br=br+cnt<rb'_r=b_r+cnt_{<r}k=kcnt<rk'=k-cnt_{<r},所以转化为查询最大的 rr 满足 maxi=lrbibrk\max_{i=l}^{r}{b'_i}-b_r\leq k

由于这里 maxi=lrbi\max_{i=l}^{r}b'_i 包含 brb'_r,不利于二分,所以可以先二分出 brbrkb'_r-b_r\leq k 的一个边界 rmaxr_{max},然后变为查询 [l,rmax][l,r_{max}]maxi=lr1bibrk\max_{i=l}^{\color{red}{r-1}}{b'_i}-b_r\leq k 的最大 rr

先把区间外的位置加上一个极大值,变成全局查询 maxi=1r1bibrk\max_{i=1}^{r-1}b'_i-b_r\leq k 的最大 rr

在线段树上维护区间 bib_i 最大值和 bib'_i 的最大值,以及 mxdmxd 表示只考虑当前的节点区间 [l,r][l,r],右子树里 bimaxj=li1bjb_i-\max_{j=l}^{i-1}{b'_j} 的最大值。mxdmxd 可以在 pushup 的时候维护。

设当前查询区间为 [L,R][L,R],区间前面 bib'_i 的最大值是 mxmx。如果 mxblsmxmxb_{ls}\leq mx,说明左子树被 mxmx 完全覆盖,所以只需要二分找到左子树里 bimxkb_i\geq mx-k 的最大 ii,同时递归右子树。

如果 mxbls<mxmxb_{ls}<mx,说明 mxmx 不会影响当前区间的右子树,通过 mxdmxd 的值判断是递归左子树还是右子树即可。

pushup 的过程同理。

时间复杂度:O(nlog2n)O(n\log^2 n)

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
148
149
150
151
152
153
154
155
156
157
158
159
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 1e5 + 5;

int n, k;
int w[kMaxN], g[kMaxN], a[kMaxN], b[kMaxN];

struct BIT {
int c[kMaxN];
void upd(int x, int v) {
for (; x <= n; x += x & -x) c[x] += v;
}
int qry(int x) {
int ret = 0;
for (; x; x -= x & -x) ret += c[x];
return ret;
}
} bit;

struct SGT {
// int _b[kMaxN];
// void build() {
// for (int i = 1; i <= n; ++i) _b[i] = b[i];
// }
// void update(int x, int l, int r, int ql, int qr, int v) {
// for (int i = ql; i <= qr; ++i) _b[i] += v;
// }
// int query(int mxr = n) {
// // for (int i = 1; i <= n; ++i) std::cerr << _b[i] << ' ';
// // std::cerr << '\n';
// int ret = 0, mx = -1e18;
// for (int i = 1; i <= mxr; ++i) {
// if (mx - b[i] <= k) ret = i;
// mx = std::max(mx, _b[i]);
// }
// return ret;
// }
int mxb[kMaxN * 4], mx_b[kMaxN * 4], tag[kMaxN * 4], mxd[kMaxN * 4];

void addtag(int x, int l, int r, int v) {
mx_b[x] += v, tag[x] += v, mxd[x] -= v;
}
void pushdown(int x, int l, int r) {
if (tag[x]) {
int mid = (l + r) >> 1;
addtag(x << 1, l, mid, tag[x]), addtag(x << 1 | 1, mid + 1, r, tag[x]);
tag[x] = 0;
}
}
int getpos(int x, int l, int r, int v) {
if (mxb[x] < v) return 0;
if (l == r) return l;
int mid = (l + r) >> 1;
if (mxb[x << 1 | 1] >= v) return getpos(x << 1 | 1, mid + 1, r, v);
else return getpos(x << 1, l, mid, v);
}
int getmxd(int x, int l, int r, int mx = -1e18) {
if (l == r) return b[l] - mx;
pushdown(x, l, r);
int mid = (l + r) >> 1;
// return std::max(query(x << 1, l, mid, mx), query(x << 1 | 1, mid + 1, r, std::max(mx, mx_b[x << 1])));
if (mx >= mx_b[x << 1]) {
return std::max(mxb[x << 1] - mx, getmxd(x << 1 | 1, mid + 1, r, mx));
} else {
return std::max(mxd[x], getmxd(x << 1, l, mid, mx));
}
}
void pushup(int x, int l, int r) {
mxb[x] = std::max(mxb[x << 1], mxb[x << 1 | 1]);
mx_b[x] = std::max(mx_b[x << 1], mx_b[x << 1 | 1]);
if (l != r) {
int mid = (l + r) >> 1;
mxd[x] = getmxd(x << 1 | 1, mid + 1, r, mx_b[x << 1]);
}
}
void build(int x, int l, int r) {
if (l == r) return void(mxb[x] = mx_b[x] = b[l]);
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
pushup(x, l, r);
}
void update(int x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag(x, l, r, v);
pushdown(x, l, r);
int mid = (l + r) >> 1;
update(x << 1, l, mid, ql, qr, v), update(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x, l, r);
}
int query(int x, int l, int r, int mx) {
if (l == r) return mx - b[l] <= k ? l : 0;
pushdown(x, l, r);
int mid = (l + r) >> 1;
if (mx >= mx_b[x << 1]) {
return std::max(getpos(x << 1, l, mid, mx - k), query(x << 1 | 1, mid + 1, r, mx));
} else {
if (mxd[x] >= -k) return query(x << 1 | 1, mid + 1, r, mx_b[x << 1]);
else return query(x << 1, l, mid, mx);
}
}
} sgt;

void upd(int x, int v) {
sgt.update(1, 1, n, x, n, v);
bit.upd(x, v);
}

void solve() {
static int stk[kMaxN];
int top = 0;
sgt.build(1, 1, n);
sgt.update(1, 1, n, 1, n, -1e18);
b[0] = -1e18;
int ans = 0;
for (int i = n; i; --i) {
for (; top && a[stk[top]] <= a[i]; --top) {
if (top > 1) upd(stk[top - 1] - 1, -(a[stk[top - 1]] - a[stk[top]]));
}
stk[++top] = i;
if (top > 1) upd(stk[top - 1] - 1, a[stk[top - 1]] - a[stk[top]]);
sgt.update(1, 1, n, i, i, 1e18);
int L = i - 1, R = n + 1, mxr = i - 1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (bit.qry(mid - 1) <= k) L = mxr = mid;
else R = mid;
}
sgt.update(1, 1, n, mxr, n, 1e18);
ans = std::max(ans, sgt.query(1, 1, n, -1e18) - i + 1);
sgt.update(1, 1, n, mxr, n, -1e18);
}
std::cout << ans << '\n';
}

void dickdreamer() {
std::cin >> n >> k;
for (int i = 1; i < n; ++i) std::cin >> w[i];
for (int i = 1; i <= n; ++i) std::cin >> g[i];
for (int i = 1; i <= n; ++i) {
a[i] = a[i - 1] + w[i - 1] - g[i - 1];
b[i] = b[i - 1] + g[i] - w[i - 1];
}
solve();
}

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;
}

CF671E Organizing a Race 题解
https://sobaliuziao.github.io/2025/02/24/post/265163bd.html
作者
Egg_laying_master
发布于
2025年2月24日
许可协议