欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF671E Organizing a Race 题解

Description

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

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

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

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

n105k,wi,gi109

Solution

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

{wlglwl+wl+1gl+gl+1i=lr1wii=lr1gi

{wr1grwr1+wr2gr+gr1i=lr1wii=l+1rgi

ak=i=1k(wi1gi1)bk=i=1k(giwi1),则需要满足 al=maxi=lraibr=maxi=lrbi

那么让 gi 加一,会让 ai+1,ai+2,,an 减一和 bi,bi+1,,bn 加一。

如果只有 a 的限制,对于固定的 l 右边的第一个 ax>al 的位置 x,需要满足 [l,x1] 这个区间至少需要操作 axal 次,操作过后对于 x 右边的第一个位置 y,同样需要满足 [x,y1] 要操作 ayax 次。

这启发我们维护一个单调栈,然后从后往前扫 l,对于栈中的元素 ci,贪心地选择在 gci1 操作 aci1aci 次。

同时通过线段树维护出操作后的 b 数组,题目转化为:区间加,查询最大的 r 满足 maxi=lrbibrk

注意到 br=br+cnt<rk=kcnt<r,所以转化为查询最大的 r 满足 maxi=lrbibrk

由于这里 maxi=lrbi 包含 br,不利于二分,所以可以先二分出 brbrk 的一个边界 rmax,然后变为查询 [l,rmax]maxi=lr1bibrk 的最大 r

先把区间外的位置加上一个极大值,变成全局查询 maxi=1r1bibrk 的最大 r

在线段树上维护区间 bi 最大值和 bi 的最大值,以及 mxd 表示只考虑当前的节点区间 [l,r],右子树里 bimaxj=li1bj 的最大值。mxd 可以在 pushup 的时候维护。

设当前查询区间为 [L,R],区间前面 bi 的最大值是 mx。如果 mxblsmx,说明左子树被 mx 完全覆盖,所以只需要二分找到左子树里 bimxk 的最大 i,同时递归右子树。

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

pushup 的过程同理。

时间复杂度:O(nlog2n)

Code

#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;
}
posted @   下蛋爷  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起