灵茶山艾府
2019-10-29 22:32:13
珂朵莉树实质上是一种可以维护区间上的分裂与合并的数据结构,但要求数据是随机的,或者有大量的随机合并操作,这样才能保证维护的区间个数是一个很小的值。
一开始,我们用不同的节点表示
本题中的“把区间
图例:横轴为操作次数,纵轴为区间个数
目前主流的实现是基于 set
来维护节点,但由于平均维护的区间个数很小,set
的优势并不明显。相比之下,链表(或数组)能更简洁地维护分裂与合并操作。
typedef long long int64;
struct Block {
Block *next; // 链表下一节点
int l, r; // 区间范围
int64 val; // 区间上的值
Block(Block *next, int l, int r, int64 val): next(next), l(l), r(r), val(val) {}
bool operator<(const Block &b) const { return val < b.val; }
} *root;
// 返回左端点为 mid+1 的区间
Block *split(int mid) {
for (Block *b = root; b; b = b->next) { // 遍历链表
if (b->l == mid + 1) { // 左端点为 mid+1
return b;
}
// 寻找能包含 mid 和 mid+1 的区间 [l, r],将其被拆分成 [l, mid] 和 [mid+1, r]
if (b->l <= mid && mid + 1 <= b->r) {
b->next = new Block(b->next, mid + 1, b->r, b->val);
b->r = mid;
return b->next;
}
}
return nullptr; // 未找到,返回空
}
在操作区间时,由于不能只维护区间的一部分,所以下面的操作进行之前都需要预先分裂区间,再完成相应操作。
Block *lb, *rb;
// 预分裂,保证后续操作在 [l, r] 内部
void prepare(int l, int r) {
lb = split(l - 1);
rb = split(r);
}
void merge(int l, int r, int64 val) {
prepare(l, r);
lb->r = r; // 将区间 [lb.l, lb.r] 修改成 [lb.l, r]
lb->val = val;
lb->next = rb; // 将 [lb.l, r] 链至其右侧相邻区间
}
// 注:这里没有释放被删除节点的内存,若有需要可自行添加
遍历统计
// 区间更新
void add(int l, int r, int64 val) {
prepare(l, r);
for (Block *b = lb; b != rb; b = b->next)
b->val += val;
}
// 区间第 k 小
int64 kth(int l, int r, int k) {
prepare(l, r);
vector<Block> blocks;
for (Block *b = lb; b != rb; b = b->next)
blocks.emplace_back(*b);
sort(blocks.begin(), blocks.end());
k--;
for (Block b: blocks) {
int cnt = b.r - b.l + 1;
if (k >= cnt) k -= cnt;
else return b.val;
}
}
// 快速幂
int64 quick_pow(int64 x, int n, int64 mod) {
x %= mod;
int64 res = 1 % mod;
for (; n; n >>= 1) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
}
return res;
}
// 区间幂和
int64 pow_sum(int l, int r, int n, int64 mod) {
prepare(l, r);
int64 sum = 0;
for (Block *b = lb; b != rb; b = b->next)
sum += int64(b->r - b->l + 1) * quick_pow(b->val, n, mod);
return sum % mod;
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long int64;
int64 seed;
// 生成 [0, n-1] 的随机数
int rand(int n) {
int64 ret = seed;
seed = (seed * 7 + 13) % int64(1e9 + 7);
return int(ret) % n;
}
struct Block {
Block *next;
int l, r;
int64 val;
Block(Block *next, int l, int r, int64 val): next(next), l(l), r(r), val(val) {}
bool operator<(const Block &b) const { return val < b.val; }
} *root, *lb, *rb;
Block *split(int mid) {
for (Block *b = root; b; b = b->next) {
if (b->l == mid + 1) {
return b;
}
if (b->l <= mid && mid + 1 <= b->r) {
b->next = new Block(b->next, mid + 1, b->r, b->val);
b->r = mid;
return b->next;
}
}
return nullptr;
}
void prepare(int l, int r) {
lb = split(l - 1);
rb = split(r);
}
void add(int64 val) {
for (Block *b = lb; b != rb; b = b->next)
b->val += val;
}
void merge(int r, int64 val) {
lb->r = r;
lb->val = val;
lb->next = rb;
}
int64 kth(int k) {
vector<Block> blocks;
for (Block *b = lb; b != rb; b = b->next)
blocks.emplace_back(*b);
sort(blocks.begin(), blocks.end());
k--;
for (Block b: blocks) {
int cnt = b.r - b.l + 1;
if (k >= cnt) k -= cnt;
else return b.val;
}
}
int64 quick_pow(int64 x, int n, int64 mod) {
x %= mod;
int64 res = 1 % mod;
for (; n; n >>= 1) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
}
return res;
}
int64 pow_sum(int n, int64 mod) {
int64 sum = 0;
for (Block *b = lb; b != rb; b = b->next)
sum += int64(b->r - b->l + 1) * quick_pow(b->val, n, mod);
return sum % mod;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, v_max;
cin >> n >> m >> seed >> v_max;
root = new Block(nullptr, 0, 0, rand(v_max) + 1);
Block *b = root;
for (int i = 1; i < n; i++) {
b->next = new Block(nullptr, i, i, rand(v_max) + 1);
b = b->next;
}
for (; m; m--) {
int op = rand(4) + 1;
int l = rand(n), r = rand(n);
if (l > r) swap(l, r);
int x = op == 3 ? rand(r - l + 1) + 1 : rand(v_max) + 1;
prepare(l, r); // 所有操作前预分裂区间
switch (op) {
case 1:
add(x);
break;
case 2:
merge(r, x);
break;
case 3:
cout << kth(x) << '\n';
break;
default:
int64 y = rand(v_max) + 1;
cout << pow_sum(x, y) << '\n';
}
}
return 0;
}