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
结尾的合法子序列数量, 进行分类讨论:
- \(s_i\) 为
0
: \(f_{i, 0} = f_{i - 1, 1} + f_{i - 1, 0}, f_{i, 1} = f_{i - 1, 1}\). - \(s_i\) 为
1
: \(f_{i, 1} = f_{i - 1, 1} + f_{i - 1, 0} + 1, f_{i, 0} = f_{i - 1, 0}\), 其中加一是因为1
可以自成一段. - \(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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现