[Ynoi2013] D2T2
分块,对于每一块用线段树的结构维护每个节点在每个值域区间下的最大子段和。由于管辖区间长度为 \(len\) 的区间只有 \(len^2\) 种值域区间,所以只要能够在 \(\Theta(len^2)\) 的复杂度内合并左右子树到当前节点就可以了。归并排序可以找到左/右儿子最大/小的比这个数大/小的数,直接合并即可。复杂度 \(\Theta(n\sqrt{n})\)。
说起来好简单,但是为什么不会呢
#include <cstdio>
#include <vector>
#include <algorithm>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 10000, stdin), p1 == p2) ? EOF : *p1 ++)
#define pc(ch) (pw < 100000 ? obuf[pw ++] = (ch) : (fwrite(obuf, 1, 100000, stdout), obuf[(pw = 0)++] = (ch)))
#define _l long long
inline int min(const int x, const int y) {return x < y ? x : y;}
inline _l min(const _l x, const _l y) {return x < y ? x : y;}
inline int max(const int x, const int y) {return x > y ? x : y;}
inline _l max(const _l x, const _l y) {return x > y ? x : y;}
inline void cmx(_l &x, const _l y) {if (x < y) x = y;}
const int S = 280;
char buf[100000], *p1, *p2, obuf[100000];
int pw;
inline int read() {
char ch; int x = 0, f = 1; while ((ch = gc) < 48) if (ch == '-') f = -1;
do x = x * 10 + ch - 48; while ((ch = gc) >= 48); return x * f;
}
inline void write(_l val) {
static char a[10]; int len = 0; do a[++ len] = val % 10 + '0', val /= 10; while (val);
while (len) pc(a[len --]); return;
}
int a[100005], bel[100005], ll[285], lr[285], rl[285], rr[285], geq[100005], leq[100005];
struct Info {_l pre, suf, mx, sum;} range[10][285][285], ans[100005];
struct node {int l, r, x, y, id;} vec[100005];
struct pair {int x, y;} b[285], c[285];
struct node2 {
int pos, id;
inline bool operator < (const node2 a) const {return pos < a.pos;}
} lp[100005], rp[100005];
void solve(int dep, int l, int r) {
if (l == r)
return range[dep][l][l] = Info{max(b[l].x, 0), max(b[l].x, 0), max(b[l].x, 0), b[l].x}, void();
int mid = l + r >> 1, p = l, q = mid + 1, cur = 0;
solve(dep + 1, l, mid), solve(dep + 1, mid + 1, r);
while (p <= mid && q <= r) c[++ cur] = (b[p].x <= b[q].x ? b[p ++] : b[q ++]);
while (p <= mid) c[++ cur] = b[p ++]; while (q <= r) c[++ cur] = b[q ++];
for (int i = l, mxl = 0, mxr = 0; i <= r; ++ i)
b[i] = c[i - l + 1], b[i].y <= mid ? mxl = i : mxr = i, ll[i] = mxl, lr[i] = mxr;
for (int i = r, mnl = 286, mnr = 286; i >= l; -- i)
b[i].y <= mid ? mnl = i : mnr = i, rl[i] = mnl, rr[i] = mnr;
for (int i = l; i <= r; ++ i)
for (int j = i; j <= r; ++ j) {
Info& tmp = range[dep][i][j],
&lft = range[dep + 1][b[rl[i]].y][b[ll[j]].y],
&rgt = range[dep + 1][b[rr[i]].y][b[lr[j]].y];
tmp.sum = lft.sum + rgt.sum;
tmp.pre = max(lft.pre, lft.sum + rgt.pre), tmp.suf = max(rgt.suf, rgt.sum + lft.suf);
tmp.mx = max(max(lft.mx, rgt.mx), lft.suf + rgt.pre);
}
for (int i = l; i <= r; ++ i) b[i].y = i;
}
inline void merge(Info& A, const Info& B) {
A.mx = max(max(A.mx, B.mx), A.suf + B.pre);
cmx(A.pre, A.sum + B.pre), A.suf = max(B.suf, A.suf + B.sum);
A.sum += B.sum;
}
signed main() {
int n = read(), q = read();
for (int i = 1; i <= n; ++ i) a[i] = read(), bel[i] = (i - 1) / S + 1;
for (int i = 1; i <= q; ++ i) {
int l = read(), r = read(), x = read(), y = read();
vec[i] = node{l, r, x, y, i};
lp[i].pos = x, lp[i].id = rp[i].id = i, rp[i].pos = y;
}
std::sort(lp + 1, lp + q + 1), std::sort(rp + 1, rp + q + 1);
for (int i = 1; i <= (n + S - 1) / S; ++ i) {
int m = 0;
for (int j = (i - 1) * S + 1; j <= i * S && j <= n; ++ j) b[++ m] = pair{a[j], j - (i - 1) * S};
solve(0, 1, m);
for (int j = q, k = m + 1; j; -- j) {
while (k > 1 && lp[j].pos <= b[k - 1].x) -- k;
geq[lp[j].id] = k;
}
for (int j = 1, k = 0; j <= q; ++ j) {
while (k < m && b[k + 1].x <= rp[j].pos) ++ k;
leq[rp[j].id] = k;
}
node j = vec[1];
for (int _ = 1; _ <= q; j = vec[++ _]) if (j.r > (i - 1) * S)
if (j.l <= (i - 1) * S + 1 && i * S <= j.r) merge(ans[j.id], range[0][geq[j.id]][leq[j.id]]);
else {
Info tmp = {0, 0, 0, 0};
for (int k = max(j.l, (i - 1) * S + 1); k <= min(j.r, i * S); ++ k)
if (j.x <= a[k] && a[k] <= j.y) {
cmx(tmp.pre, tmp.sum += a[k]);
if ((tmp.suf += a[k]) < 0) tmp.suf = 0;
tmp.mx = max(tmp.mx, tmp.suf);
}
merge(ans[j.id], tmp);
}
}
for (int i = 1; i <= q; ++ i) write(ans[i].mx), pc('\n');
fwrite(obuf, 1, pw, stdout);
return 0;
}