CF722C Destroying Array 題解
阿新 • • 發佈:2022-04-08
根據目前作者瞭解,本題有兩種解法:並查集/線段樹。
1. 並查集
對於並查集這種只能合併不能刪除的資料結構,當然要想一點珂技。
我們倒過來思考,將刪除操作變為插入操作,相鄰點合併。
考慮到序列裡面的數都為正數,那麼直接取出子樹權值和最大的即可。
預計時間複雜度 \(O(n)\),上界 \(O(n \log n)\)。
2. 線段樹
這個需要模仿 GSS 系列,維護字首和,字尾和,總和,最大子段和,然後刪除操作處理成最小值。這個方法支援數為負數的情況。
沒了解過 GSS 系列的讀者可以看一看這篇文章:線段樹演算法總結&專題訓練3
時間複雜度 \(O(n \log n)\),上下界也為 \(O(n \log n)\)
程式碼:(只有線段樹)
#include <bits/stdc++.h> #define Max(a, b) ((a > b) ? a : b) using namespace std; typedef long long LL; const int MAXN = 1e5 + 10; const LL inf = -1e14; int n, a[MAXN]; struct node { int l, r, flag; LL sum, pre, aft, maxn; #define l(p) tree[p].l #define r(p) tree[p].r #define s(p) tree[p].sum #define p(p) tree[p].pre #define a(p) tree[p].aft #define m(p) tree[p].maxn node operator +(const node &fir) { node sec; sec.l = l; sec.r = fir.r; sec.sum = sum + fir.sum; sec.pre = Max(pre, sum + fir.pre); sec.aft = Max(fir.aft, fir.sum + aft); sec.maxn = Max(fir.maxn, Max(maxn, aft + fir.pre)); sec.flag = flag | fir.flag; return sec; } }tree[MAXN << 2]; int read() { int sum = 0, fh = 1; char ch = getchar(); while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar();} while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();} return sum * fh; } void build(int p, int l, int r) { l(p) = l, r(p) = r; if (l == r) {s(p) = a(p) = p(p) = m(p) = a[l]; tree[p].flag = 1; return ;} int mid = (l + r) >> 1; build (p << 1, l, mid); build(p << 1 | 1, mid + 1, r); tree[p] = tree[p << 1] + tree[p << 1 | 1]; } void deal(int p, int x) { if (l(p) == r(p) && r(p) == x) { tree[p].flag = 0; s(p) = m(p) = a(p) = p(p) = inf; return ; } int mid = (l(p) + r(p)) >> 1; if (x <= mid) deal(p << 1, x); else deal(p << 1 | 1, x); tree[p] = tree[p << 1] + tree[p << 1 | 1]; } int main() { n = read(); for (int i = 1; i <= n; ++i) a[i] = read(); build(1, 1, n); for (int i = 1; i <= n; ++i) { int x = read(); deal(1, x); printf("%lld\n", Max(0ll, tree[1].maxn)); } return 0; }