【线段树维护矩阵】HDU1006最甜的小情侣

HDU1006最甜的小情侣

题意

给你一个环形数组 $ a_1, a_2, \ldots, a_n $,要求你支持以下操作:

  1. 查询:计算从数组中选出若干元素的最大和,满足:
    • 不能选择连续超过 \(3\) 个元素(环形,即首尾也视为连续)。
  2. 修改:将 $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;
}
posted @ 2025-08-13 21:24  Ke_scholar  阅读(23)  评论(0)    收藏  举报