feat: 添加5911.cpp和P3391.cpp两个算法实现文件

5911.cpp实现了一个动态规划解决方案,用于解决特定条件下的最优组合问题
P3391.cpp实现了一个基于Treap数据结构的区间反转操作,支持高效区间操作
This commit is contained in:
Zengtudor 2025-10-04 20:48:04 +08:00
parent 4ccd04aeb3
commit e70c668e57
2 changed files with 223 additions and 0 deletions

38
src/10/4/5911.cpp Normal file
View File

@ -0,0 +1,38 @@
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <istream>
using ll = int64_t;
const ll maxn = (1 << 16) + 5, inf = 1e9 + 7;
ll W, n;
ll max[maxn], w[maxn], dp[maxn];
int main() {
std::iostream::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> W >> n;
for (ll i = 0; i < n; i++) {
std::cin >> max[1 << i] >> w[1 << i];
}
for (ll i = 3; i < (1 << n); i++) {
const ll lb = i & -i;
max[i] = std::max(max[lb], max[i ^ lb]);
w[i] = w[lb] + w[i ^ lb];
}
std::fill(dp, dp + (1 << n), inf);
dp[0] = 0;
for (ll S = 1; S < (1 << n); ++S) {
for (ll P = S; P; P = (P - 1) & S) {
if (w[P] <= W) {
ll pS = S ^ P;
if (dp[pS] != inf) {
dp[S] = std::min(dp[S], dp[pS] + max[P]);
}
}
}
}
std::cout << dp[(1 << n) - 1] << "\n";
}

185
src/10/4/P3391.cpp Normal file
View File

@ -0,0 +1,185 @@
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
// 最大节点数N + 1 (留出下标0作为空节点)
const int MAXN = 200005;
// 节点结构体
struct Node {
int l, r; // 左右子节点的数组下标 (代替指针)
int val; // 节点存储的值 (本题中为 1 到 N)
int size; // 以该节点为根的子树大小
int rnd; // 随机优先级 (维护堆性质)
bool rev; // 反转延迟标记 (Lazy Tag)
} tree[MAXN];
int root = 0; // Treap 的根节点下标
int idx = 0; // 节点分配计数器
// --- 辅助函数 ---
// 1. 获取子树大小
int get_size(int p) {
return p ? tree[p].size : 0;
}
// 2. 信息更新 (更新子树大小)
void push_up(int p) {
if (!p) return;
tree[p].size = get_size(tree[p].l) + get_size(tree[p].r) + 1;
}
// 3. 标记下传 (Pushdown)
// 当需要访问或修改子节点时,必须先将父节点的标记下传
void push_down(int p) {
if (!p || !tree[p].rev) return;
// 交换左右子树 (结构上的反转)
swap(tree[p].l, tree[p].r);
// 向子节点传递反转标记
if (tree[p].l) tree[tree[p].l].rev ^= 1;
if (tree[p].r) tree[tree[p].r].rev ^= 1;
// 清除当前节点的标记
tree[p].rev = false;
}
// 4. 节点创建
int new_node(int v) {
idx++;
tree[idx].val = v;
tree[idx].size = 1;
tree[idx].rnd = rand(); // C++ rand() 简单实现,实际竞赛需更优随机数
tree[idx].l = tree[idx].r = 0;
tree[idx].rev = false;
return idx;
}
// --- 核心操作 ---
// 5. 合并 (Merge) 操作
// 将 t1 和 t2 合并,要求 t1 中所有元素在中序遍历上都在 t2 之前
// 返回合并后的新树根下标
int merge(int t1, int t2) {
if (!t1 || !t2) return t1 + t2; // 如果其中一个为空,返回另一个
// 合并前,先下传标记
push_down(t1);
push_down(t2);
// 比较优先级 (rnd),优先级高的作为新根
if (tree[t1].rnd > tree[t2].rnd) {
// t1 作为根t1的右子树与 t2 合并
tree[t1].r = merge(tree[t1].r, t2);
push_up(t1); // 更新 t1 的 size
return t1;
} else {
// t2 作为根t1 与 t2 的左子树合并
tree[t2].l = merge(t1, tree[t2].l);
push_up(t2); // 更新 t2 的 size
return t2;
}
}
// 6. 分裂 (Split) 操作 (按大小 k 分裂)
// 将 p 分裂成 t1 (前 k 个元素) 和 t2 (剩余元素)
// 注意t1 和 t2 是通过引用传递的,会直接修改外部变量
void split(int p, int k, int &t1, int &t2) {
if (!p) {
t1 = t2 = 0;
return;
}
// 分裂前,先下传标记
push_down(p);
int s_l = get_size(tree[p].l); // 左子树的大小
if (k <= s_l) {
// 当前节点 p 及其右子树都属于 t2t1 只在 p 的左子树中
split(tree[p].l, k, t1, tree[p].l); // 递归分裂左子树
t2 = p; // p 成为 t2 的根 (已连接其左子树为 tree[p].l)
} else {
// 当前节点 p 及其左子树都属于 t1t2 只在 p 的右子树中
// k' = k - s_l - 1 是需要在右子树中寻找的元素个数
split(tree[p].r, k - s_l - 1, tree[p].r, t2); // 递归分裂右子树
t1 = p; // p 成为 t1 的根 (已连接其右子树为 tree[p].r)
}
push_up(p); // 更新 p 的 size
}
// --- 区间反转操作 ---
// 7. 区间反转 [l, r]
void reverse_interval(int l, int r) {
int t1, t2, t3;
// (1) 分裂出左侧部分 [1, l-1] -> t1
// t1: [1, l-1] | t2: [l, N]
split(root, l - 1, t1, t2);
// (2) 分裂出中间部分 [l, r] -> t2
// t2: [l, r] | t3: [r+1, N]
split(t2, r - l + 1, t2, t3);
// (3) 对中间部分打上反转标记
if (t2) {
tree[t2].rev ^= 1;
}
// (4) 合并回去
// root = t1 | (t2 | t3)
root = merge(t1, merge(t2, t3));
}
// --- 8. 中序遍历输出结果 ---
void print_inorder(int p) {
if (!p) return;
// 访问前,先下传标记,确保结构正确
push_down(p);
print_inorder(tree[p].l);
cout << tree[p].val << " ";
print_inorder(tree[p].r);
}
// --- 主函数 ---
int main() {
// 设置随机数种子
srand(time(0));
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n, m;
cin >> n >> m;
// 初始化序列: 1, 2, 3, ..., N
for (int i = 1; i <= n; ++i) {
// 依次将新节点合并到 Treap 的最右端
root = merge(root, new_node(i));
}
// 执行 M 次操作
for (int i = 0; i < m; ++i) {
int l, r;
cin >> l >> r;
reverse_interval(l, r);
}
// 输出最终序列
print_inorder(root);
cout << endl;
return 0;
}