2025.1.9 CW 模拟赛

题面 & 题解

T2

算法

动态规划, 博弈论.

思路

假设梦梦不能操作, 我们可以用动态规划求出前后缀的最大子段和, 这是 \(\mathcal{O}(n)\)​ 的.

具体来说, 令 \(pre_i\) 表示以 \(i\) 结尾的最大子段和, \(premx_i\) 表示 \([1, i]\) 这段区间的最大子段和 (\(suf_i, sufmx_i\) 为后缀, 同理).

如果 ta 操作了 \(2i - 1, 2i\), 那么新的最大子段和只需要在 \([1, 2i - 2], [2i + 1, r]\) 以及包含 \(2i - 1, 2i\) 中恰好一个或者两个 (跨过) 中查询. 前者已经用 DP 预处理, 后者合并起来即可, 总时间复杂度 \(\mathcal{O}(n)\).

#include "iostream"
#include "algorithm"

using namespace std;

constexpr int N = 1e5 + 10;

#define int long long

int n, a[N], b[N];

void init() {
	cin >> n;
	for (int i = 1; i <= n; ++i) cin >> a[i];
	for (int i = 1; i <= n; ++i) cin >> b[i];
}

int pre[N], premx[N], suf[N], sufmx[N];

void calculate() {
	fill(pre, pre + n + 2, 0), fill(premx, premx + n + 2, 0);
	fill(suf, suf + n + 2, 0), fill(sufmx, sufmx + n + 2, 0);
	for (int i = 1; i <= n; ++i) {
		pre[i] = max({0ll, a[i], b[i], pre[i - 1] + a[i] + b[i]});
		premx[i] = max({pre[i], premx[i - 1], pre[i - 1] + a[i], pre[i - 1] + b[i]});
	}
	for (int i = n; i; --i) {
		suf[i] = max({0ll, a[i], b[i], suf[i + 1] + a[i] + b[i]});
		sufmx[i] = max({suf[i], sufmx[i + 1], suf[i + 1] + a[i], suf[i + 1] + b[i]});
	}
	int ans = 1e18;
	for (int i = 1; i <= n; ++i) for (int j = 0; j ^ 2; ++j) {
		int t = max({premx[i - 1], sufmx[i + 1], pre[i - 1] + a[i], suf[i + 1] + b[i], pre[i - 1] + suf[i + 1] + a[i] + b[i]});
		ans = min(ans, t), swap(a[i], b[i]);
	}
	cout << ans << '\n';
}

void solve() {
	init();
	calculate();
}

signed main() {
	cin.tie(nullptr)->sync_with_stdio(false);
	int t; cin >> t;
	while (t--) solve();
	return 0;
}

T3

算法

动态规划, 矩阵, 线段树.

思路

先考虑 \(\mathcal{O}(nq)\) 做法.

\(f_{i, 0 / 1}\) 表示到第 \(i\) 位以 0/1 结尾的合法子序列数量, 进行分类讨论:

  1. \(s_i\)0 : \(f_{i, 0} = f_{i - 1, 1} + f_{i - 1, 0}, f_{i, 1} = f_{i - 1, 1}\).
  2. \(s_i\)1 : \(f_{i, 1} = f_{i - 1, 1} + f_{i - 1, 0} + 1, f_{i, 0} = f_{i - 1, 0}\), 其中加一是因为 1 可以自成一段.
  3. \(s_i\)_ : \(f_{i, 1} = f_{i - 1, 1}, f_{i, 0} = f_{i - 1, 0}\).

优化

转移方程式相同的, 考虑用矩阵优化.

对于 \(s_i\)0:

\[\begin{bmatrix} f_{i, 0} & f_{i, 1} & 1 \end{bmatrix} = \begin{bmatrix} f_{i - 1, 0} & f_{i - 1, 1} & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 \\ 1 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

对于 \(s_i\)1:

\[\begin{bmatrix} f_{i, 0} & f_{i, 1} & 1 \end{bmatrix} = \begin{bmatrix} f_{i - 1, 0} & f_{i - 1, 1} & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 \\ 1 & 1 & 0 \\ 1 & 0 & 1 \end{bmatrix} \]

对于 \(s_i\)_:

\[\begin{bmatrix} f_{i, 0} & f_{i, 1} & 1 \end{bmatrix} = \begin{bmatrix} f_{i - 1, 0} & f_{i - 1, 1} & 1 \end{bmatrix} \cdot \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} \]

矩阵满足结合律, 这里又要用到区间修改以及查询, 可以考虑使用线段树套矩阵进行维护.

具体来说, 线段树的每一个节点为一个矩阵, \(\textrm{pushdown, pushup}\) 操作与普通线段树无异, 在进行区间覆盖的时候, 打懒标记即可.

#include "iostream"
#include "cstring"

using namespace std;

constexpr int N = 1e5 + 10, mod = 998244353;

int n, q;
string s;

struct Matrix {
	int m[3][3];
	Matrix() { memset(m, 0, sizeof m); }
	friend Matrix operator*(Matrix x, Matrix y) {
		Matrix ans;
		for (int i = 0; i ^ 3; ++i)
			for (int k = 0; k ^ 3; ++k)
				for (int j = 0; j ^ 3; ++j)
					ans.m[i][j] = (ans.m[i][j] + 1ll * x.m[i][k] * y.m[k][j]) % mod;
		return ans;
	}
} base, base0, base1;

void init() {
	cin >> n >> q >> s;
	s = ' ' + s;
	base.m[0][0] = base.m[1][1] = base.m[2][2] = 1;
	base0 = base, base0.m[1][0] = 1;
	base1 = base, base1.m[0][1] = base1.m[2][1] = 1;
}

class Segment_Tree {
protected:
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
	struct Node {
		Matrix x;
		bool cover;
	} tr[N << 2];
	
#define pushup(rt) tr[rt].x = tr[rt << 1].x * tr[rt << 1 | 1].x
	
#define cover(rt) tr[rt].cover = 1, tr[rt].x = base
	
	void pushdown(int rt) {
		if (tr[rt].cover)
			cover(rt << 1), cover(rt << 1 | 1);
	}
	
public:
	void build(int rt, int l, int r) {
		tr[rt].cover = 0;
		if (l == r) {
			if (s[l] == '1') tr[rt].x = base1;
			else tr[rt].x = base0;
			return;
		}
		int mid = (l + r) >> 1;
		build(lson), build(rson);
		pushup(rt);
	}
	
	void update(int rt, int l, int r, int L, int R) {
		if (l >= L and r <= R) 
			return cover(rt), void();
		pushdown(rt);
		int mid = (l + r) >> 1;
		if (L <= mid) update(lson, L, R);
		if (mid < R) update(rson, L, R);
		pushup(rt);
	}
	
	Matrix query(int rt, int l, int r, int L, int R) {
		if (!R) return base;
		if (l >= L and r <= R) return tr[rt].x;
		pushdown(rt);
		int mid = (l + r) >> 1;
		Matrix ans = base;
		if (L <= mid)
			ans = ans * query(lson, L, R);
		if (mid < R)
			ans = ans * query(rson, L, R);
		return ans;
	}
	
} st;

void print(Matrix x) { 
	for (int i = 0; i ^ 3; ++i, cout << '\n')
		for (int j = 0; j ^ 3; ++j) cout << x.m[i][j] << ' ';
}

void calculate() {
	st.build(1, 1, n);
	while (q--) {
		int op, l, r;
		cin >> op >> l >> r;
		if (op & 1)
			st.update(1, 1, n, l, r);
		else {
			Matrix ans = st.query(1, 1, n, l, r);
			print(ans);
			cout << (ans.m[2][0] + ans.m[2][1]) % mod << '\n';
		}
	}
}

void solve() {
	init();
	calculate();
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);
	solve();
	return 0;
}
posted @   Steven1013  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示