cf671e/soj809 贪心/二分探索/神仙
题目链接:
https://codeforces.ml/contest/671/problem/E
本题的思路不是我想出来的。。但是思路的发明者lk在最后出现了一些漏洞,并不能通过所有的测试点。所以我在他的基础上进行修正,以及证明复杂度是
以下会先稍加修改基本完全复制他(lk)的博客。
https://qaq-am.com/CF671E/
题解
首先考虑没有k限制的情况(或者说加完之后)如何判断l,r是否可以办比赛。
将题意中的
设
那么l,r可以办比赛当且仅当
那么考虑有k限制的情况。
如果
从
那么为了l到r单向的比赛满足条件,肯定是
接下来考虑
设
显然
如果对于
可以在单调栈二分出
考虑我在线段树上二分查询的过程
int qry(int l, int r, int p, int id, ll x) {
if (r - l == 1) return l;
pd(id);
int mid = (l + r) >> 1; ll to = min(x, dat[2 * id + 1]);
if (mid <= p && to >= mn[2 * id + 2] - k) {
int ans = qry(mid, r, p, 2 * id + 2, to);
if (ans >= 0) return ans;
}
if (mn[2 * id + 1] - k <= x) return qry(l, mid, p, 2 * id + 1, x);
return -1;
}
表示我当前在线段树上访问的区间是
结论:当
证明:我们加油的地方所在的区间一定在
我们对
若
若
因此我们的单次询问的操作是
因此单次询问复杂度就是
#include <bits/stdc++.h>
#define ld double
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define iiii pair<int, pii>
#define mp make_pair
#define INF 1000000000
#define MOD 1000000007
#define rep(i, x) for(int (i) = 0; (i) < (x); (i)++)
#define getchar() (*input_pos++)
const int TT = 22000050;
char input_buffer[TT], output_buffer[TT];
char *input_pos = input_buffer, *output_pos = output_buffer;
inline int getint() {
int x = 0, p = 1; char c = getchar();
while (c <= 32) c = getchar();
if (c == 45) p = -p, c = getchar();
while (c > 32) x = x * 10 + c - 48, c = getchar();
return x * p;
}
inline void write(int x) {
if (!x) return;
write(x / 10);
(*output_pos++) = x % 10 + '0';
}
using namespace std;
//ruogu_alter
const int N = 1e6 + 5;
const ll inf = 2e18;
int n, k, w[N], d[N], res[N];
ll a[N], b[N], dat[N << 2], tag[N << 2], mn[N << 2];
vector<int> st;
//
inline void add(int k, ll x) {
dat[k] += x;
tag[k] += x;
}
inline void pd(int k) {
if (tag[k]) {
add(2 * k + 1, tag[k]);
add(2 * k + 2, tag[k]);
tag[k] = 0;
}
}
void upd(int l, int r, int k, int p) {
if (r - l == 1) {
mn[k] = dat[k] = b[p];
return;
}
int mid = (l + r) >> 1;
if (p < mid) upd(l, mid, 2 * k + 1, p);
else upd(mid, r, 2 * k + 2, p);
dat[k] = min(dat[2 * k + 1], dat[2 * k + 2]);
mn[k] = min(mn[2 * k + 1], mn[2 * k + 2]);
}
void modify(int l, int r, int x, int y, int k, ll v) {
if (l >= y || x >= r) return;
if (x <= l && r <= y) {
add(k, v);
return;
}
pd(k);
int mid = (l + r) >> 1;
modify(l, mid, x, y, 2 * k + 1, v);
modify(mid, r, x, y, 2 * k + 2, v);
dat[k] = min(dat[2 * k + 1], dat[2 * k + 2]);
}
int qry(int l, int r, int p, int id, ll x) {
if (r - l == 1) return l;
pd(id);
int mid = (l + r) >> 1; ll to = min(x, dat[2 * id + 1]);
if (mid <= p && to >= mn[2 * id + 2] - k) {
int ans = qry(mid, r, p, 2 * id + 2, to);
if (ans >= 0) return ans;
}
if (mn[2 * id + 1] - k <= x) return qry(l, mid, p, 2 * id + 1, x);
return -1;
}
int main() {
rep(i, N << 2) mn[i] = -inf, dat[i] = inf;
fread(input_buffer, 1, TT, stdin);
n = getint(); k = getint();
rep(i, n - 1) d[i] = getint();
rep(i, n) w[i] = getint();
for (int i = 1; i < n; i++) a[i] = a[i - 1] + w[i - 1] - d[i - 1];
b[0] = -w[0];
for (int i = 1; i < n; i++) b[i] = b[i - 1] - w[i] + d[i - 1];
st.emplace_back(n); a[n] = -inf;
int ans = 0;
for (int i = n - 1; i >= 0; i--) {
upd(0, n, 0, i);
while (st.size() && a[st.back()] >= a[i]) {
if (st.size() > 2) modify(0, n, st[st.size() - 2] - 1, n, 0, a[st.back()] - a[st[st.size() - 2]]);
st.pop_back();
}
int lb = 0, rb = st.size();
while (rb - lb > 1) {
int mid = (lb + rb) >> 1;
if (a[st[mid]] + k < a[i]) lb = mid;
else rb = mid;
}
if (st.size() > 1) modify(0, n, st[st.size() - 1] - 1, n, 0, a[st[st.size() - 1]] - a[i]);
st.emplace_back(i);
res[i] = qry(0, n, st[lb] - 1, 0, inf);
}
rep(i, n) {
write(res[i] + 1);
*output_pos++ = ' ';
}
fwrite(output_buffer, 1, output_pos - output_buffer, stdout);
return 0;
}