洛谷9877 [EC Final 2021] Vacation 题解(线段树)
将数列按 \(c\) 分块,则一个查询 \([l,r]\) 的贡献由以下几部分组成:
-
\([l,r]\) 之间的整块的最大子段和。
-
\(l\) 到第一个整块之间的最大子段和。
-
最后一个整块到 \(r\) 的最大子段和。
-
整块与整块之间的跨边界的最大子段和。
-
\(l\) 与第一个整块之间跨边界的最大子段和。
-
最后一个整块与 \(r\) 之间跨边界的最大子段和。
我们知道,整块内部的最大子段和是好维护的(小白逛公园),接下来考虑怎么维护跨边界的贡献。考虑设 \(f_i\) 表示当前块的前 \(i\) 个数之和,\(g_j\) 表示上一个块的后 \(j\) 个数之和,最后的答案就是 \(\max_{i+j\le c} f_i+g_j\)。
像这种形式的 \((\max,+)\) 卷积,一个用线段树维护的方法是令 \(g'_x = g_{c-x}\),将答案转化为 \(\max_{i<j} f_i+g_j\)。这个东西是线段树可维护的,我们将 \(f\) 整体右移一位,将所求式子转为 \(\max_{i\le j} f_i+g_j\),然后每个节点维护对应区间的内的 \(\max f_i, \max g_i, \max_{i\le j} f_i+g_j\),在 pushup 的时候考虑当前的左儿子的 \(\max f\) 与右儿子的 \(\max g\) 相加是否更优即可。可以证明,这样维护是正确的。
维护这个 \((\max, +)\) 卷积的线段树还需要支持区间修改(因为我们每次改相当于是更新了 \(f\) 的一个后缀, \(g\) 的一个前缀),考虑标记与信息的合并。可以证明,一个区间 \(f_i'=f_i+v\),最终的答案也一定为 \(ans'=ans+v\),\(g_i\) 同理。
对于整块之间的答案,由于每次查询的时候可能会跨很多整块,所以我们在外面再开一个线段树,维护整块之间的答案 \(\max\) 即可。
对于散块的贡献,用左端点举例,相当于是有一部分 \(g\) 的后缀可用,其所有的 \(f\) 都可用,则我们可以令中间这个端点为 \(x\),统计 \([x, R']\) 的答案与 \(\max g\)、\([L',x-1]\) 的 \(\max f\),最后用 \(\max f+\max g\) 更新答案。右端点同理。
最终时间复杂度 \(O(n \log n)\)。
const int MAXN = 2e5 + 5;
int n, m, c, a[MAXN];
struct _sgt_zdh {
int N;
struct _node {
int sm, mx, lmx, rmx;
};
vector<_node> tr;
void init(int _N) {
N = _N;
tr.resize(N * 4);
}
void pushup(int p) {
tr[p].sm = tr[lson].sm + tr[rson].sm;
tr[p].mx = max({tr[lson].mx, tr[rson].mx, tr[lson].rmx + tr[rson].lmx});
tr[p].lmx = max(tr[lson].lmx, tr[lson].sm + tr[rson].lmx);
tr[p].rmx = max(tr[rson].rmx, tr[rson].sm + tr[lson].rmx);
}
void modify(int p, int l, int r, int k, int v) {
if (l == r) {
tr[p].sm = tr[p].mx = tr[p].lmx = tr[p].rmx = v;
return;
}
if (k <= mid) modify(lson, l, mid, k, v);
else modify(rson, mid + 1, r, k, v);
pushup(p);
}
auto query(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return tr[p];
auto merge = [](_node ls, _node rs) {
_node ret;
ret.sm = ls.sm + rs.sm;
ret.mx = max({ls.mx, rs.mx, ls.rmx + rs.lmx});
ret.lmx = max(ls.lmx, ls.sm + rs.lmx);
ret.rmx = max(rs.rmx, rs.sm + ls.rmx);
return ret;
};
_node ret = {0, INT_MIN, INT_MIN, INT_MIN};
if (L <= mid) ret = query(lson, l, mid, L, R);
if (mid < R) ret = merge(ret, query(rson, mid + 1, r, L, R));
return ret;
}
};
struct _sgt_conv {
int N;
struct _node {
int f, g, sm;
int tagf, tagg;
};
vector<_node> tr;
void pushup(int p) {
tr[p].f = max(tr[lson].f, tr[rson].f);
tr[p].g = max(tr[lson].g, tr[rson].g);
tr[p].sm = max({tr[lson].sm, tr[rson].sm, tr[lson].f + tr[rson].g});
}
void init(int _N) {
N = _N;
tr.resize(N * 4);
}
void addtagf(int p, int v) {
tr[p].tagf += v;
tr[p].sm += v;
tr[p].f += v;
}
void addtagg(int p, int v) {
tr[p].tagg += v;
tr[p].sm += v;
tr[p].g += v;
}
void pushdown(int p) {
if (tr[p].tagf) {
addtagf(lson, tr[p].tagf);
addtagf(rson, tr[p].tagf);
tr[p].tagf = 0;
}
if (tr[p].tagg) {
addtagg(lson, tr[p].tagg);
addtagg(rson, tr[p].tagg);
tr[p].tagg = 0;
}
}
void modifyf(int p, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) return addtagf(p, v);
pushdown(p);
if (L <= mid) modifyf(lson, l, mid, L, R, v);
if (mid < R) modifyf(rson, mid + 1, r, L, R, v);
pushup(p);
}
void modifyg(int p, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) return addtagg(p, v);
pushdown(p);
if (L <= mid) modifyg(lson, l, mid, L, R, v);
if (mid < R) modifyg(rson, mid + 1, r, L, R, v);
pushup(p);
}
auto query(int p, int l, int r, int L, int R) {
if (L > R) return (_node){INT_MIN, INT_MIN, INT_MIN};
if (L <= l && r <= R) return tr[p];
auto merge = [](_node u, _node v) {
_node ret;
ret.f = max(u.f, v.f);
ret.g = max(u.g, v.g);
ret.sm = max({u.sm, v.sm, u.f + v.g});
return ret;
};
pushdown(p);
_node res = {INT_MIN, INT_MIN, INT_MIN};
if (L <= mid) res = query(lson, l, mid, L, R);
if (mid < R) res = merge(res, query(rson, mid + 1, r, L, R));
return res;
}
};
struct _sgt_max {
int N;
struct _node {
int mx;
};
vector<_node> tr;
void pushup(int p) {
tr[p].mx = max(tr[lson].mx, tr[rson].mx);
}
void init(int _N) {
N = _N;
tr.resize(N * 4);
}
void modify(int p, int l, int r, int k, int v) {
if (l == r) return tr[p].mx = v, void();
if (k <= mid) modify(lson, l, mid, k, v);
else modify(rson, mid + 1, r, k, v);
pushup(p);
}
int query(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return tr[p].mx;
int res = INT_MIN;
if (L <= mid) res = max(res, query(lson, l, mid, L, R));
if (mid < R) res = max(res, query(rson, mid + 1, r, L, R));
return res;
}
} tans;
int L[MAXN], R[MAXN], bel[MAXN];
vector<_sgt_zdh> tzdh;
vector<_sgt_conv> tconv;
void work() {
cin >> n >> m >> c;
for (int i = 1; i <= n; ++i)
cin >> a[i];
int B = (n + c - 1) / c;
for (int i = 1; i <= B; ++i) {
L[i] = (i - 1) * c + 1;
R[i] = min(n, i * c);
}
for (int i = 1; i <= B; ++i) {
for (int j = L[i]; j <= R[i]; ++j) {
bel[j] = i;
}
}
tzdh.resize(B + 1);
for (int i = 1; i <= B; ++i) {
tzdh[i].init(R[i] - L[i] + 1);
for (int j = L[i]; j <= R[i]; ++j) {
tzdh[i].modify(1, 1, R[i] - L[i] + 1, j - L[i] + 1, a[j]);
}
}
tconv.resize(B + 1);
for (int i = 1; i <= B; ++i) {
tconv[i].init(c);
for (int j = 1; j <= R[i] - L[i] + 1; ++j) {
int x = L[i] + j - 1;
tconv[i].modifyf(1, 0, c, j, c, a[x]);
}
if (i > 1) {
for (int j = 1; j <= R[i - 1] - L[i - 1] + 1; ++j) {
int x = L[i - 1] + j - 1;
if (j != 1) tconv[i].modifyg(1, 0, c, 0, j - 1, a[x]);
}
}
}
tans.init(2 * B);
for (int i = 1; i <= B; ++i) {
tans.modify(1, 1, 2 * B, 2 * i, tzdh[i].tr[1].mx);
if (i > 1) {
tans.modify(1, 1, 2 * B, 2 * i - 1, tconv[i].tr[1].sm);
}
}
while (m--) {
int op; cin >> op;
if (op == 1) {
int x, y; cin >> x >> y;
tzdh[bel[x]].modify(1, 1, tzdh[bel[x]].N, x - L[bel[x]] + 1, y);
tans.modify(1, 1, 2 * B, 2 * bel[x], tzdh[bel[x]].tr[1].mx);
tconv[bel[x]].modifyf(1, 0, c, x - L[bel[x]] + 1, c, y - a[x]);
tans.modify(1, 1, 2 * B, 2 * bel[x] - 1, tconv[bel[x]].tr[1].sm);
if (bel[x] != B) {
if (x != L[bel[x]]) tconv[bel[x] + 1].modifyg(1, 0, c, 0, x - L[bel[x]], y - a[x]);
tans.modify(1, 1, 2 * B, 2 * bel[x] + 1, tconv[bel[x] + 1].tr[1].sm);
}
a[x] = y;
} else {
int l, r; cin >> l >> r;
int lx = l - L[bel[l]] + 1, rx = r - L[bel[r]] + 1;
if (bel[l] == bel[r]) {
cout << max(tzdh[bel[l]].query(1, 1, tzdh[bel[l]].N, lx, rx).mx, 0ll) << endl;
continue;
}
int ans = 0;
if (bel[l] + 1 < bel[r]) {
ans = max(ans, tans.query(1, 1, 2 * B, (bel[l] + 1) * 2, (bel[r] - 1) * 2));
auto resR = tconv[bel[l] + 1].query(1, 0, c, lx - 1, c);
auto resL = tconv[bel[l] + 1].query(1, 0, c, 0, lx - 2);
ans = max(ans, max(resR.sm, resL.f + resR.g));
resL = tconv[bel[r]].query(1, 0, c, 0, rx);
resR = tconv[bel[r]].query(1, 0, c, rx + 1, c);
ans = max(ans, max(resL.sm, resL.f + resR.g));
} else {
auto resL = tconv[bel[r]].query(1, 0, c, 0, min(rx, lx - 2));
auto resR = tconv[bel[r]].query(1, 0, c, max(rx + 1, lx - 1), c);
ans = max(ans, resR.g + resL.f);
if (rx >= lx - 1) {
auto res = tconv[bel[r]].query(1, 0, c, lx - 1, rx);
ans = max(ans, max({res.sm, res.g + resL.f, resR.g + res.f}));
}
}
ans = max(ans, tzdh[bel[l]].query(1, 1, tzdh[bel[l]].N, l - L[bel[l]] + 1, tzdh[bel[l]].N).mx);
ans = max(ans, tzdh[bel[r]].query(1, 1, tzdh[bel[r]].N, 1, r - L[bel[r]] + 1).mx);
cout << ans << endl;
}
}
}

浙公网安备 33010602011771号