洛谷 P8987 [北大集训 2021] 简单数据结构
考虑若原来的序列是不降的,那么进行 \(1\) 操作或 \(2\) 操作序列仍然不降。那么 \(1\) 操作直接线段树上二分然后打覆盖标记,\(2\) 操作直接打标记即可。
考虑一般情况,发现某个时刻所有被 \(1\) 操作影响过的 \(i\)(存在一次 \(1\) 操作使得 \(a_i \ge v\))组成的集合 \(S\),\(S\) 内部的序列是单调的。
于是整个序列被分成了两部分:\(S\) 和 \([1, n] \setminus S\)。对于 \(S\),\(1\) 操作直接线段树上二分然后打标记。\(2\) 操作就分别对两个部分打个标记即可。
线段树一个结点需要维护:\(S\) 部分的最大值及其最大下标(为了 \(2\) 操作后计算新的最大值),两部分分别的和及其下标的和,\(S\) 部分的元素个数和这个区间在 \(S\) 部分的最大下标(打覆盖标记时需要将最大值的最大下标重置为这个值)。
剩下最后一个问题:如何计算一个元素何时加入 \(S\)。考虑二分 \(mid\),相当于查询是否 \(\exists j \in [1, mid], b_j \times i + a_i \ge v_j\)(其中 \(b_i\) 为第 \(i\) 次 \(1\) 操作前面的 \(2\) 操作次数)。变形得 \(\min\limits_{j = 1}^{mid} \{ -b_j \times i + v_j \} \le a_i\)。考虑整体二分,维护一个 \((b_j, v_j)\) 的下凸包,check 就维护一个凸包上的指针即可。因为 \(b_j\) 单调,所以不用排序。注意可能存在若干个 \(b_j\) 相等,这种取它们 \(v_j\) 的最小值即可。
总时间复杂度 \(O(n \log n)\)。
code
// Problem: #3673. 「北大集训 2021」简单数据结构
// Contest: LibreOJ
// URL: https://loj.ac/p/3673
// Memory Limit: 512 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 200100;
const ll inf = 0x3f3f3f3f3f3f3f3fLL;
ll n, m, a[maxn], tot, f[maxn], id[maxn];
vector<int> vc[maxn];
struct pig {
ll op, x, y;
} qq[maxn];
struct wwh {
ll x, y;
wwh(ll a = 0, ll b = 0) : x(a), y(b) {}
} p[maxn], stk[maxn];
inline wwh operator + (const wwh &a, const wwh &b) {
return wwh(a.x + b.x, a.y + b.y);
}
inline wwh operator - (const wwh &a, const wwh &b) {
return wwh(a.x - b.x, a.y - b.y);
}
inline ll operator * (const wwh &a, const wwh &b) {
return a.x * b.y - a.y * b.x;
}
void dfs(int l, int r, vector<int> Q) {
if (l > r || Q.empty()) {
return;
}
if (l == r) {
for (int i : Q) {
f[i] = (p[l].y - i * p[l].x <= a[i] ? l : 0);
}
return;
}
int mid = (l + r) >> 1, top = 0;
for (int i = l, j = l; i <= mid; i = (++j)) {
while (j < mid && p[j + 1].x == p[i].x) {
++j;
}
ll mn = 9e18;
for (int k = i; k <= j; ++k) {
mn = min(mn, p[k].y);
}
wwh u(p[i].x, mn);
while (top >= 2 && (stk[top] - stk[top - 1]) * (u - stk[top - 1]) <= 0) {
--top;
}
stk[++top] = u;
}
vector<int> L, R;
int j = 1;
for (int i : Q) {
while (j < top && (stk[j + 1].y - stk[j].y) <= i * (stk[j + 1].x - stk[j].x)) {
++j;
}
if (stk[j].y - i * stk[j].x <= a[i]) {
L.pb(i);
} else {
R.pb(i);
}
}
dfs(l, mid, L);
dfs(mid + 1, r, R);
}
struct node {
ll mx, mxi, sv0, sv1, si0, si1, cnt, i1;
};
inline node operator + (const node &a, const node &b) {
node res;
if (a.mx > b.mx) {
res.mx = a.mx;
res.mxi = a.mxi;
} else {
res.mx = b.mx;
res.mxi = b.mxi;
}
res.sv0 = a.sv0 + b.sv0;
res.sv1 = a.sv1 + b.sv1;
res.si0 = a.si0 + b.si0;
res.si1 = a.si1 + b.si1;
res.cnt = a.cnt + b.cnt;
res.i1 = max(a.i1, b.i1);
return res;
}
namespace SGT {
ll add0[maxn << 2], add1[maxn << 2], tag[maxn << 2];
node a[maxn << 2];
inline void pushup(int x) {
a[x] = a[x << 1] + a[x << 1 | 1];
}
inline void pushadd(int x, ll y, ll z) {
a[x].sv0 += a[x].si0 * y;
add0[x] += y;
if (a[x].cnt) {
a[x].mx += a[x].mxi * z;
a[x].sv1 += a[x].si1 * z;
add1[x] += z;
}
}
inline void pushtag(int x, ll y) {
if (!a[x].cnt) {
return;
}
a[x].mx = y;
a[x].mxi = a[x].i1;
a[x].sv1 = y * a[x].cnt;
add1[x] = 0;
tag[x] = y;
}
inline void pushdown(int x) {
if (tag[x] != -1) {
pushtag(x << 1, tag[x]);
pushtag(x << 1 | 1, tag[x]);
tag[x] = -1;
}
if (add0[x] || add1[x]) {
pushadd(x << 1, add0[x], add1[x]);
pushadd(x << 1 | 1, add0[x], add1[x]);
add0[x] = add1[x] = 0;
}
}
void build(int rt, int l, int r) {
a[rt].mx = -inf;
tag[rt] = -1;
if (l == r) {
a[rt].sv0 = ::a[l];
a[rt].si0 = l;
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void assign(int rt, int l, int r, int ql, int qr, ll x) {
if (ql <= l && r <= qr) {
pushtag(rt, x);
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (ql <= mid) {
assign(rt << 1, l, mid, ql, qr, x);
}
if (qr > mid) {
assign(rt << 1 | 1, mid + 1, r, ql, qr, x);
}
pushup(rt);
}
void update(int rt, int l, int r, int x) {
if (l == r) {
a[rt].mx = a[rt].sv1 = ::a[l];
a[rt].mxi = a[rt].si1 = l;
a[rt].sv0 = a[rt].si0 = 0;
a[rt].cnt = 1;
a[rt].i1 = l;
return;
}
pushdown(rt);
int mid = (l + r) >> 1;
(x <= mid) ? update(rt << 1, l, mid, x) : update(rt << 1 | 1, mid + 1, r, x);
pushup(rt);
}
ll query(int rt, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return a[rt].sv0 + a[rt].sv1;
}
pushdown(rt);
int mid = (l + r) >> 1;
ll res = 0;
if (ql <= mid) {
res += query(rt << 1, l, mid, ql, qr);
}
if (qr > mid) {
res += query(rt << 1 | 1, mid + 1, r, ql, qr);
}
return res;
}
int find(int rt, int l, int r, ll x) {
if (a[rt].mx <= x) {
return -1;
}
if (l == r) {
return l;
}
pushdown(rt);
int mid = (l + r) >> 1;
if (a[rt << 1].mx > x) {
return find(rt << 1, l, mid, x);
} else {
return find(rt << 1 | 1, mid + 1, r, x);
}
}
}
void solve() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
}
int cnt = 0;
for (int i = 1; i <= m; ++i) {
scanf("%lld", &qq[i].op);
if (qq[i].op == 1) {
scanf("%lld", &qq[i].x);
p[++tot] = wwh(cnt, qq[i].x);
id[tot] = i;
} else if (qq[i].op == 2) {
++cnt;
} else {
scanf("%lld%lld", &qq[i].x, &qq[i].y);
}
}
vector<int> S;
for (int i = 1; i <= n; ++i) {
S.pb(i);
}
dfs(1, tot, S);
for (int i = 1; i <= n; ++i) {
vc[id[f[i]]].pb(i);
}
SGT::build(1, 1, n);
for (int i = 1; i <= m; ++i) {
if (qq[i].op == 1) {
int p = SGT::find(1, 1, n, qq[i].x);
if (p != -1) {
SGT::assign(1, 1, n, p, n, qq[i].x);
}
for (int j : vc[i]) {
a[j] = qq[i].x;
SGT::update(1, 1, n, j);
}
} else if (qq[i].op == 2) {
SGT::pushadd(1, 1, 1);
} else {
printf("%lld\n", SGT::query(1, 1, n, qq[i].x, qq[i].y));
}
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}