mirror of
https://gitcode.com/Zengtudor/alg2025.git
synced 2025-10-17 21:42:25 +00:00
feat: 添加5911.cpp和P3391.cpp两个算法实现文件
5911.cpp实现了一个动态规划解决方案,用于解决特定条件下的最优组合问题 P3391.cpp实现了一个基于Treap数据结构的区间反转操作,支持高效区间操作
This commit is contained in:
parent
4ccd04aeb3
commit
e70c668e57
38
src/10/4/5911.cpp
Normal file
38
src/10/4/5911.cpp
Normal 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
185
src/10/4/P3391.cpp
Normal 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 及其右子树都属于 t2,t1 只在 p 的左子树中
|
||||
split(tree[p].l, k, t1, tree[p].l); // 递归分裂左子树
|
||||
t2 = p; // p 成为 t2 的根 (已连接其左子树为 tree[p].l)
|
||||
} else {
|
||||
// 当前节点 p 及其左子树都属于 t1,t2 只在 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user