【线段树维护矩阵】HDU1006最甜的小情侣
HDU1006最甜的小情侣
题意
给你一个环形数组 $ a_1, a_2, \ldots, a_n $,要求你支持以下操作:
- 查询:计算从数组中选出若干元素的最大和,满足:
- 不能选择连续超过 \(3\) 个元素(环形,即首尾也视为连续)。
- 修改:将 $a_x $ 的值改为 $ v $。
初始时以及每次修改后,都需要输出当前的最大和。
题解
用线段树维护区间信息,每个节点存储一个 \(4 \times 4\) 的 DP 矩阵 \(a[i][j]\),其中:
- \(i\) 表示从区间左端点开始的连续选择长度 (\(0 \leq i \leq 3\))
- \(j\) 表示以区间右端点结束的连续选择长度 (\(0 \leq j \leq 3\))
状态转移方程
1. 叶子节点(单个元素)
\[a[1][1] = a_x, \quad a[0][0] = 0
\]
2. 区间合并(设 \(U\) 为父节点,\(L\) 为左子区间,\(R\) 为右子区间)
\[\begin{cases}
U_{i+j,i+j} \leftarrow L_{i,i} + R_{j,j} & \text{if } i = |L| \land j = |R| \land i+j \leq 3 \\
U_{i+k,j} \leftarrow L_{i,i} + R_{k,j} & \text{if } i = |L| \land k \leq 3-i \\
U_{i,j+k} \leftarrow L_{i,k} + R_{j,j} & \text{if } j = |R| \land k \leq 3-j \\
U_{i,j} \leftarrow \max\limits_{k=0}^{3} \left( L_{i,k} + \max\limits_{l=0}^{3-k} R_{l,j} \right) & \text{otherwise}
\end{cases}
\]
最终答案取根节点所有满足 \(i + j \leq 3\) 的状态最大值:
\[\text{ans} = \max_{\substack{0 \leq i,j \leq 3 \\ i+j \leq 3}} a[i][j]
\]
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
template<class Node>
struct SegmentTree {
#define lc u<<1
#define rc u<<1|1
const int n, N;
vector<Node> tr;
SegmentTree(): n(0) {}
SegmentTree(int n_): n(n_), N(n * 4 + 10) {
tr.reserve(N);
tr.resize(N);
}
SegmentTree(vector<int> init) : SegmentTree(init.size()) {
function<void(int, int, int)> build = [&](int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u] = {l, r};
tr[u].a[1][1] = init[l - 1];
return ;
}
i64 mid = (l + r) >> 1;
build(lc, l, mid);
build(rc, mid + 1, r);
pushup(tr[u], tr[lc], tr[rc]);
};
build(1, 1, n);
}
void pushup(Node& U, Node& L, Node& R) { //上传
U.l = L.l, U.r = R.r;
int Len = L.r - L.l + 1, Ren = R.r - R.l + 1;
for (int i = 0; i <= min(3, Len); i += 1) {
for (int j = 0; j <= min(3, Ren); j += 1) {
if (i == Len && j == Ren) {
if (i + j > 3) {
continue;
}
U.a[i + j][i + j] = L.a[i][i] + R.a[j][j];
}
else if (i == Len) {
for (int k = 0; k <= min(3 - i, Ren); k += 1) {
U.a[i + k][j] = L.a[i][i] + R.a[k][j];
}
}
else if (j == Ren) {
for (int k = 0; k <= min(3 - j, Len); k += 1) {
U.a[i][j + k] = L.a[i][k] + R.a[j][j];
}
}
else {
i64 Max = 0;
U.a[i][j] = 0;
for (int k = 3; k >= 0; k -= 1) {
Max = max(Max, R.a[3 - k][j]);
U.a[i][j] = max(U.a[i][j], L.a[i][k] + Max);
}
}
}
}
}
void modify(int u, int l, int r, int k) {
if (tr[u].l >= l && tr[u].r <= r) {
tr[u].a[1][1] = k;
return ;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) {
modify(lc, l, r, k);
}
if (r > mid) {
modify(rc, l, r, k);
}
pushup(tr[u], tr[lc], tr[rc]);
}
Node query(int u, int l, int r) { //区查
if (l <= tr[u].l && tr[u].r <= r) {
return tr[u];
}
i64 mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) {
return query(lc, l, r);
}
if (l > mid) {
return query(rc, l, r);
}
Node U;
Node L = query(lc, l, r), R = query(rc, l, r);
pushup(U, L, R);
return U;
}
};
struct Node { //线段树定义
int l, r;
i64 a[4][4] {};
};
void solve() {
int n,q;
cin >> n >> q;
vector<int> a(n);
for (int i = 0; i < n; i += 1) {
cin >> a[i];
}
SegmentTree<Node> S(a);
auto Answer = [&]()->i64{
i64 res = 0;
for (int i = 0; i <= 3; i += 1) {
for (int j = 0; j <= 3 - i; j += 1) {
res = max(res, S.tr[1].a[i][j]);
}
}
return res;
};
cout << Answer() << "\n";
while (q--) {
int x, v;
cin >> x >> v;
S.modify(1, x, x, v);
cout << Answer() << "\n";
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}

浙公网安备 33010602011771号