Description
你有一個長度為 \(n\) 的序列 \(a\),下面你要進行 \(q\) 次修改或詢問。
- 給定 \(v\),將所有 \(a_i\) 變為 \(\min(a_i, v)\)。
- 將所有 \(a_i\) 變為 \(a_i + i\)。
- 給定 \(l, r\),詢問 \(\sum_{i=l}^r a_i\)。
\(1\leq n,q\leq 2\times 10^5,0\leq a_i,v_i\leq 10^{12}\)。
Solution
首先如果沒有 chkmin 操作是好做的。同時如果 \(a_i\) 初始值為 \(0\) 那麼從始至終序列 \(a\) 都是單調不降的,對於 chkmin 操作只要二分+區間賦值,也很好做。
考慮 \(a_i\) 初值不為 \(0\) 的情況。
注意到如果某個時刻 \(x<y\) 且 \(a_x\leq a_y\),那麼之後的所有時刻都滿足 \(a_x\leq a_y\),這是顯然的。
然後就可以發現對於所有 chkmin 過的位置,它們的 \(a\) 值一定單調不降。於是只要維護當前 chkmin 過的位置,對於這些二分+區間賦值。沒有 chkmin 的位置直接線段樹維護即可。
考慮怎麼求出每個 \(a_i\) 第一次被 chkmin 的時間。設 \(v_i\) 表示第 \(i\) 次操作 chkmin 的值,\(c_i\) 表示前 \(i\) 次操作做了多少次二操作。
容易發現對於每個 \(i\),可以二分答案 \(p\),那麼對於 \([1,p]\) 裡的所有操作,如果存在 \(a_i+i\cdot c_j\geq v_j\) 就說明 \(p\) 可行。移項一下可以得到 \(a_i\geq v_j-i\cdot c_j\),右邊是個關於 \(i\) 的一次函式,所以可以對於前 \(p\) 個操作按照 \((c_j,v_j)\) 維護下凸殼,然後用 \(k=i\) 的直線去截這個凸殼即可快速得到答案。
但是這麼做是 \(O\left(nq\log^2 n\right)\) 的,可以用整體二分最佳化到 \(O\left(q\log^2 n\right)\)。
時間複雜度:\(O\left((n+q)\log^2 n\right)\)。
Code
#include <bits/stdc++.h>
#define int int64_t
using pii = std::pair<int, int>;
const int kMaxN = 2e5 + 5;
int n, q;
int a[kMaxN], op[kMaxN], l[kMaxN], r[kMaxN], v[kMaxN];
std::vector<int> vv[kMaxN];
std::vector<std::tuple<int, int, int>> chkm;
pii add(pii a, pii b) { return {a.first + b.first, a.second + b.second}; }
pii sub(pii a, pii b) { return {a.first - b.first, a.second - b.second}; }
int64_t mul(pii a, pii b) {
return 1ll * a.first * b.second - 1ll * a.second * b.first;
}
int64_t func(int k, pii a) { return -1ll * k * a.first + a.second; }
void solve(std::vector<std::tuple<int, int, int>> vec, std::vector<int> id) {
if (!vec.size()) return;
if (vec.size() == 1) {
auto [j, c, v] = vec[0];
for (auto i : id) {
if (a[i] >= v - i * c) {
vv[j].emplace_back(i);
}
}
return;
}
std::vector<pii> pt, stk;
std::vector<int> Lv, Rv;
std::vector<std::tuple<int, int, int>> lvec, rvec;
for (int i = 0; i < vec.size() / 2; ++i) {
lvec.emplace_back(vec[i]);
pt.emplace_back(std::get<1>(vec[i]), std::get<2>(vec[i]));
}
for (int i = vec.size() / 2; i < vec.size(); ++i) rvec.emplace_back(vec[i]);
std::sort(pt.begin(), pt.end());
for (auto p : pt) {
for (; stk.size() >= 2 &&
mul(sub(p, stk.back()), sub(stk.back(), stk[stk.size() - 2])) >= 0;
stk.pop_back()) {
}
stk.emplace_back(p);
}
for (auto i : id) {
int L = 0, R = stk.size(), res = 0;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (func(i, stk[mid]) <= func(i, stk[mid - 1]))
L = res = mid;
else
R = mid;
}
if (func(i, stk[res]) <= a[i])
Lv.emplace_back(i);
else
Rv.emplace_back(i);
}
solve(lvec, Lv), solve(rvec, Rv);
}
void prework() {
std::vector<int> id(n);
std::iota(id.begin(), id.end(), 1);
solve(chkm, id);
}
struct SGT {
int cnt[kMaxN * 4];
int64_t mx[kMaxN * 4], mxid[kMaxN * 4], sum[kMaxN * 4], sumid[kMaxN * 4],
tag[kMaxN * 4], tagcov[kMaxN * 4];
void pushup(int x) {
sum[x] = sum[x << 1] + sum[x << 1 | 1];
sumid[x] = sumid[x << 1] + sumid[x << 1 | 1];
mx[x] = std::max(mx[x << 1], mx[x << 1 | 1]);
mxid[x] = std::max(mxid[x << 1], mxid[x << 1 | 1]);
cnt[x] = cnt[x << 1] + cnt[x << 1 | 1];
}
void addtag1(int x, int64_t v) {
if (!cnt[x]) return;
sum[x] += sumid[x] * v, tag[x] += v;
mx[x] += mxid[x] * v;
}
void addtag2(int x, int64_t v) {
if (!cnt[x]) return;
sum[x] = cnt[x] * v, mx[x] = tagcov[x] = v;
tag[x] = 0;
}
void pushdown(int x) {
if (~tagcov[x]) {
addtag2(x << 1, tagcov[x]), addtag2(x << 1 | 1, tagcov[x]);
tagcov[x] = -1;
}
if (tag[x]) {
addtag1(x << 1, tag[x]), addtag1(x << 1 | 1, tag[x]);
tag[x] = 0;
}
}
void build(int x, int l, int r, bool op = 1) {
tagcov[x] = -1;
if (l == r) {
if (op) {
cnt[x] = 1, sumid[x] = mxid[x] = l;
sum[x] = a[l];
} else {
mxid[x] = mx[x] = -1;
}
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid, op), build(x << 1 | 1, mid + 1, r, op);
pushup(x);
}
void update1(int x, int l, int r, int ql, int v, bool op = 1) {
if (l == r) {
if (op) {
tag[x] = 0, tagcov[x] = -1, cnt[x] = 1, sumid[x] = mxid[x] = l;
addtag2(x, v);
} else {
cnt[x] = sum[x] = sumid[x] = mxid[x] = tag[x] = 0;
tagcov[x] = mx[x] = -1;
}
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if (ql <= mid)
update1(x << 1, l, mid, ql, v, op);
else
update1(x << 1 | 1, mid + 1, r, ql, v, op);
pushup(x);
}
void update2(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 addtag2(x, v);
pushdown(x);
int mid = (l + r) >> 1;
update2(x << 1, l, mid, ql, qr, v),
update2(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}
int getpos(int x, int l, int r, int v) {
if (mx[x] < v) return 0;
if (l == r) return l;
pushdown(x);
int mid = (l + r) >> 1;
if (mx[x << 1] >= v)
return getpos(x << 1, l, mid, v);
else
return getpos(x << 1 | 1, mid + 1, r, v);
}
int64_t query(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql)
return 0;
else if (l >= ql && r <= qr)
return sum[x];
pushdown(x);
int mid = (l + r) >> 1;
return query(x << 1, l, mid, ql, qr) +
query(x << 1 | 1, mid + 1, r, ql, qr);
}
void print(int x, int l, int r) {
if (l == r) return void(std::cerr << sum[x] << ' ');
pushdown(x);
int mid = (l + r) >> 1;
print(x << 1, l, mid), print(x << 1 | 1, mid + 1, r);
}
void print() {
print(1, 1, n);
std::cerr << '\n';
}
} sgt[2];
void dickdreamer() {
std::cin >> n >> q;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
int nowcnt = 0;
for (int i = 1; i <= q; ++i) {
std::cin >> op[i];
if (op[i] == 1) {
std::cin >> v[i];
chkm.emplace_back(i, nowcnt, v[i]);
} else if (op[i] == 2) {
++nowcnt;
} else {
std::cin >> l[i] >> r[i];
}
}
prework();
sgt[0].build(1, 1, n, 0), sgt[1].build(1, 1, n, 1);
for (int i = 1; i <= q; ++i) {
if (op[i] == 1) {
for (auto x : vv[i]) {
sgt[0].update1(1, 1, n, x, v[i]);
sgt[1].update1(1, 1, n, x, 0, 0);
}
int p = sgt[0].getpos(1, 1, n, v[i]);
if (p) sgt[0].update2(1, 1, n, p, n, v[i]);
} else if (op[i] == 2) {
sgt[0].addtag1(1, 1), sgt[1].addtag1(1, 1);
} else {
std::cout << sgt[0].query(1, 1, n, l[i], r[i]) +
sgt[1].query(1, 1, n, l[i], r[i])
<< '\n';
}
}
}
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;
}