[北大集训 2021] 简单数据结构
简单数据结构,但本蒟蒻觉得并不简单呐!
容易发现这题的几个好用的性质:
-
只要被第一个操作影响的都能够保持单调,容易一起维护。
-
操作都是全局的!
-
没被操作一影响的都可以表示为 \(ki+a_i\) 的形式。
利用这些性质,我们考虑把没被操作一影响的项放在 \(S\) 集合,被操作一影响的项放在 \(T\) 集合。现在我们考虑动态维护这两个东西,事实上我们可以先知道每一项是在什么时候从 \(S\) 进入 \(T\) 的(显然初始每一项都在 \(S\))。
每次我们进行操作一时,都会把所有满足 \(a_i\ge v-ki\) 的所有项并入 \(T\),这种一次函数的形式启发我们维护一个凸包,上面每个点为 \((i,a_i)\),然后维护一个上凸包,但是凸包太大了,不好修改,怎么办?我们发现删点与重构凸包复杂度非常不均,所以考虑分块,我们这样每次删掉一个点,再 \(\mathcal{O}(\sqrt n)\) 重构即可。因为只会删掉 \(n\) 个点,所以复杂度是对的,但是这里有个小问题,就是我们单次操作找每个块内凸包的点有可能退化到 \(\mathcal{O}(n)\),没关系,我们的 \(k\) 是单调的,每个块内维护个双指针即可。
对答案的贡献用树状数组可以简单维护。
然后考虑维护 \(T\),首先对于 \(1\) 操作相当于是一段后缀推平,线段树上二分随便做。对于操作二的贡献在线段树上维护增加次数 \(delta\),并且维护可提供贡献的点 \(sum\),因为线段树上可以产生贡献的点并不连续。对于最大值的维护也是简单的,知道区间中最右边的在 \(T\) 中的点就知道最大值了。
时间复杂度 \(\mathcal{O}(n\sqrt n)\),这里 \(n,q\) 同级。
代码:
#include <bits/stdc++.h>
#define int long long
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define inf 1000000000
#define id(x, y) n * (x - 1) + y
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 2e5 + 5, M = (1ll << 31) - 1, P = 998244353;
constexpr double PI = acos (-1.0);
inline int rd () {
int x = 0, f = 1;
char ch = getchar ();
while (! isdigit (ch)) {
if (ch == '-') f = -1;
ch = getchar ();
}
while (isdigit (ch)) {
x = (x << 1) + (x << 3) + ch - 48;
ch = getchar ();
}
return x * f;
}
int qpow (int x, int y) {
int ret = 1;
for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
return ret;
}
namespace seg {
class node {
public:
int sum, val, mx, R, cnt, tag1, tag2;
} t[N << 2];
void clear () {
rep (i, 1, N * 4 - 1) {
t[i].sum = t[i].val = 0;
t[i].mx = t[i].R = t[i].cnt = 0;
t[i].tag1 = - 1; t[i].tag2 = 0;
}
}
void merge (node &ret, node x, node y) {
ret.sum = x.sum + y.sum;
ret.val = x.val + y.val;
ret.mx = max (x.mx, y.mx);
ret.R = max (x.R, y.R);
ret.cnt = x.cnt + y.cnt;
}
void cov (int p, int val) {
t[p].val = t[p].cnt * val;
t[p].mx = val;
t[p].tag1 = val;
t[p].tag2 = 0;
}
void add (int p, int x) {
t[p].val += t[p].sum * x;
t[p].mx += t[p].R * x;
t[p].tag2 += x;
}
void psd (int p) {
if (t[p].tag1 > -1) {
cov (ls, t[p].tag1);
cov (rs, t[p].tag1);
t[p].tag1 = -1;
}
if (t[p].tag2) {
add (ls, t[p].tag2);
add (rs, t[p].tag2);
t[p].tag2 = 0;
}
}
int find (int p, int l, int r, int x) {
if (l == r) {
if (t[p].val >= x) return l; else return 0;
}
int mid = l + r >> 1;
psd (p);
if (t[ls].mx >= x) return find (ls, l, mid, x);
else return find (rs, mid + 1, r, x);
}
void modify (int p, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) return cov (p, v), void ();
int mid = l + r >> 1; psd (p);
if (L <= mid) modify (ls, l, mid, L, R, v);
if (R > mid) modify (rs, mid + 1, r, L, R, v);
merge (t[p], t[ls], t[rs]);
}
void upd (int p, int l, int r, int x, int k) {
if (l == r) {
t[p].sum = x, t[p].cnt = 1;
t[p].R = x, t[p].val = t[p].mx = k;
return ;
} psd (p); int mid = l + r >> 1;
if (x <= mid) upd (ls, l, mid, x, k);
else upd (rs, mid + 1, r, x, k);
merge (t[p], t[ls], t[rs]);
}
int qry (int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return t[p].val;
int mid = l + r >> 1, ret = 0; psd (p);
if (L <= mid) ret += qry (ls, l, mid, L, R);
if (R > mid) ret += qry (rs, mid + 1, r, L, R);
return ret;
}
}
int n, m, q;
int a[N];
struct FWT {
int c[N];
int lb (int x) { return x & -x; }
void upd (int x, int y) {
for (; x <= n; c[x] += y, x += lb (x)) ;
}
int qry (int x) {
int ret = 0;
for (; x; ret += c[x], x -= lb (x)) ;
return ret;
}
} s, t;
class node {
public:
int opt;
int l, r;
} g[N];
class Dot {
public:
int x, y;
friend Dot operator + (const Dot &a, const Dot &b) {
return (Dot) {a.x + b.x, a.y + b.y};
}
friend Dot operator - (const Dot &a, const Dot &b) {
return (Dot) {a.x - b.x, a.y - b.y};
}
friend int operator * (const Dot &a, const Dot &b) {
return a.x * b.y - a.y * b.x;
}
} d[N];
vector <int> vec[N];
class block {
public:
int l, r, ld, rd;
int stk[505];
void build () {
l = 1, r = 0;
rep (i, ld, rd) {
if (d[i].y < 0) continue;
while (l < r && (d[stk[r]] - d[stk[r - 1]]) * (d[i] - d[stk[r]]) >= 0) -- r;
stk[++ r] = i;
}
}
void del (int k, int id, int x) {
while (l <= r) {
while (l < r && d[stk[l]].x * k + d[stk[l]].y <= d[stk[l + 1]].x * k + d[stk[l + 1]].y) ++ l;
if (d[stk[l]].x * k + d[stk[l]].y >= x) vec[id].eb (d[stk[l]].x), d[stk[l]].y = -1; else return ;
build ();
}
}
} bl[505];
void init () {
int B = sqrt (n);
m = n / B + (bool) (n % B);
rep (i, 1, m) {
bl[i].ld = bl[i - 1].rd + 1;
bl[i].rd = min (bl[i - 1].rd + B, n);
bl[i].build ();
}
}
signed main () {
// freopen ("1.in", "r", stdin);
// freopen ("1.out", "w", stdout);
n = rd (), q = rd ();
rep (i, 1, n) a[i] = rd (), d[i] = (Dot) {i, a[i]};
init ();
int now = 0;
rep (i, 1, q) {
g[i].opt = rd ();
if (g[i].opt == 1) {
g[i].l = rd ();
rep (j, 1, m) bl[j].del (now, i, g[i].l);
} else if (g[i].opt == 2) {
++ now;
} else g[i].l = rd (), g[i].r = rd ();
}
rep (i, 1, n) {
s.upd (i, i), t.upd (i, a[i]);
}
seg::clear ();
now = 0;
rep (i, 1, q) {
if (g[i].opt == 1) {
int pos = seg::find (1, 1, n, g[i].l);
if (pos) seg::modify (1, 1, n, pos, n, g[i].l);
for (auto p : vec[i]) {
s.upd (p, - p), t.upd (p, - a[p]);
seg::upd (1, 1, n, p, g[i].l);
}
} else {
if (g[i].opt == 2) {
++ now; seg::add (1, 1);
} else {
int l = g[i].l, r = g[i].r;
printf ("%lld\n", now * (s.qry (r) - s.qry (l - 1)) + t.qry (r) - t.qry (l - 1) + seg::qry (1, 1, n, l, r));
}
}
}
}