CF671E Organizing a Race
题意
题解
单调栈,线段树二分,其实也可以看成是线段树形的单调栈,具体看 LGOJ4198 楼房重建
首先考虑没有 限制的情况,如何判断
可不可以办比赛,我们设第
个加油站的油量为
,第
到
之间的道路长度为
。
我们再设 ,
显然充要条件为 且
现在多出了 个扩大
的机会,考虑在满足
能到达
的情况下,尽可能的把加油站往后放。我们设
,那么
形成一棵每个点只有
or
个儿子的多条链形结构。对于一个起点
,沿着链向下一直走,设经过的点为
,其中
,最优方案显然为
,剩余的
中没有用掉的部分肯定全部堆在
上最有可能成功,易知
。
我么设 为
被修改之后的
的值,显然如果我们只走到
,
,然后修改
相当于把
的
都增加,
端特殊处理一下。
然后就是单调栈维护 了,在单调栈被修改的之后维护
,然后线段树二分
的合法最大值。然而这只线段树解决了从
走到
,所以还要先在单调栈中二分出最远能从
走到的地方,之后再在这个范围内线段树二分。
代码
#include <bits/stdc++.h>
using namespace std;
namespace IO {
const int SIZE = 1 << 20;
char buf[SIZE + 10], *iS, *iT;
inline char Getc() {
return iS == iT && (iT = (iS = buf) + fread(buf, 1, SIZE, stdin), iS == iT) ? EOF : *iS++;
}
template <class TT>
inline void Read(TT &x) {
x = 0; register char cc = '\0'; TT fff = 1;
for (; cc < '0' || cc > '9'; cc = Getc())
if (cc == '-') fff = -1;
for (; cc >= '0' && cc <= '9'; cc = Getc())
x = (x << 1) + (x << 3) + (cc & 15);
x *= fff;
}
}
using IO::Read;
typedef long long LL;
const int N = 1e5 + 10;
int n, K, fans, Top, Stack[N], a[N], b[N];
LL f[N], g[N];
namespace Seg {
#define lc (x << 1)
#define rc (x << 1 | 1)
LL Tree[N * 4 + 10], Original[N * 4 + 10], Lazy[N * 4 + 10];
inline void Pushup(int x) { if (x) Tree[x] = max(Tree[lc], Tree[rc]); }
inline void Pushdown(int x) {
if (!Lazy[x]) return;
Lazy[lc] += Lazy[x], Tree[lc] += Lazy[x];
Lazy[rc] += Lazy[x], Tree[rc] += Lazy[x];
Lazy[x] = 0;
}
void Build(LL *A, int x = 1, int nl = 1, int nr = n) {
if (nl == nr) {
Tree[x] = Original[x] = A[nl];
return;
}
int nm = (nl + nr) >> 1;
Build(A, lc, nl, nm), Build(A, rc, nm + 1, nr);
Original[x] = max(Original[lc], Original[rc]), Pushup(x);
}
void Update(int el, int er, LL ad, int x = 1, int nl = 1, int nr = n) {
if (el <= nl && nr <= er) {
Tree[x] += ad, Lazy[x] += ad;
return;
}
int nm = (nl + nr) >> 1;
Pushdown(x);
if (el <= nm) Update(el, er, ad, lc, nl, nm);
if (er > nm) Update(el, er, ad, rc, nm + 1, nr);
Pushup(x);
}
int wantx, wantnl, wantnr;
LL premaxh, wantmaxh;
int FinalFind(int x, int nl, int nr, LL lim) {
if (nl == nr) {
// if (lim <= Tree[x] + K)
/* FinalFind 的每一步都保证了下一层有解,上面的那个就没必要了 */
return nr;
}
int nm = (nl + nr) >> 1;
Pushdown(x);
if (max(lim, Tree[lc]) <= Original[rc] + K) {
/* 易知此时右边必有一可行解(不一定最优),不必递归左边,上面条件也是冲要的 */
/* 有的题目右边不一定存在可行解这里要加上 findans = nm!!! */
return FinalFind(rc, nm + 1, nr, max(lim, Tree[lc]));
}
return FinalFind(lc, nl, nm, lim);
}
void PreFind(int el, int er, int x = 1, int nl = 1, int nr = n) {
if (el <= nl && nr <= er) {
if (premaxh <= Original[x] + K)
wantmaxh = premaxh, wantx = x, wantnl = nl, wantnr = nr;
premaxh = max(premaxh, Tree[x]);
return;
}
int nm = (nl + nr) >> 1;
Pushdown(x);
if (el <= nm)
PreFind(el, er, lc, nl, nm);
if (er > nm)
PreFind(el, er, rc, nm + 1, nr);
}
LL Query(int ed, int x = 1, int nl = 1, int nr = n) {
if (nl == nr) return Tree[x];
int nm = (nl + nr) >> 1;
Pushdown(x);
if (ed <= nm) return Query(ed, lc, nl, nm);
else return Query(ed, rc, nm + 1, nr);
}
#undef lc
#undef rc
} // namespace Seg
int main()
{
freopen("CF671E.in", "r", stdin);
freopen("CF671E.out", "w", stdout);
Read(n), Read(K);
for (int i = 1; i < n; ++i)
Read(b[i]);
for (int i = 1; i <= n; ++i)
Read(a[i]);
f[0] = f[1] = g[0] = g[1] = 0;
for (int i = 2; i <= n; ++i) {
f[i] = f[i - 1] + (a[i - 1] - b[i - 1]);
g[i] = g[i - 1] + (a[i] - b[i - 1]);
}
Seg::Build(g);
Stack[++Top] = n, fans = 1;
for (int i = n - 1; i >= 1; --i) {
while (Top > 0 && f[Stack[Top]] >= f[i]) {
if (Top != 1)
Seg::Update(Stack[Top - 1] - 1, n, -(f[Stack[Top]] - f[Stack[Top - 1]]));
--Top;
}
if (Top != 0)
Seg::Update(Stack[Top] - 1, n, f[i] - f[Stack[Top]]);
Stack[++Top] = i;
int L = 1, R = Top, mid = 0, fps = n + 1;
while (L <= R) {
mid = (L + R) >> 1;
if (f[i] - f[Stack[mid]] > K) fps = Stack[mid], L = mid + 1;
else R = mid - 1;
}
Seg::premaxh = Seg::wantmaxh = -0x3f3f3f3f3f3f3f3fLL;
Seg::wantx = 1, Seg::wantnl = 1, Seg::wantnr = n;
Seg::PreFind(i, fps - 1);
int rightpos = Seg::FinalFind(Seg::wantx, Seg::wantnl, Seg::wantnr, Seg::wantmaxh);
// fprintf(stderr, "%d : %d\n", i, rightpos);
// printf(" : %d %d %lld : %d \n", Seg::wantnl, Seg::wantnr, Seg::wantmaxh, rightpos);
fans = max(fans, rightpos - i + 1);
}
printf("%d\n", fans);
return 0;
}
查看15道真题和解析