珂朵莉樹
這個名字我猜是來源於初次誕生這個演算法的題目->Willem, Chtholly and Seniorious
演算法
適用於資料隨機, 並且有區間推平操作, 也就是區間賦值操作, 就可以用set維護, 達到優秀的 \(O(nlogn)\) 時間複雜度。
定義
struct Node{
int l, r;
mutable int v;
Node(int l, int r = 0, int v = 0) : l(l), r(r), v(v) {}
bool operator < (const Node &x) const {
return l < x.l;
}
};
set<Node> odt;
每個節點代表一個區間
split操作
就是把 \(x\) 所在區間分裂成 \([l, x - 1]\) 和 \([x, r]\)。
auto split(int x) {
auto it = odt.lower_bound(Node(x));
if (it != odt.end() && it->l == x) return it;
--it;
if (it->r < x) return odt.end();
int l = it->l, r = it->r, v = it->v;
odt.erase(it);
odt.insert(Node(l, x - 1, v));
return odt.insert(Node(x, r, v)).first;
}
assign操作
就是把 \([l, r]\) 區間重構了, 需要先把 \(l\) 和 \(r + 1\) split一下, 才能剛好提出 \([l, r]\) 這個區間, 然後暴力把這之間的全部推平。
void assign(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
odt.erase(itl, itr);
odt.insert(Node(l, r, v));
}
例題
Willem, Chtholly and Seniorious
其他各種操作都直接暴力搞, 時間複雜度在資料隨機的時候都很優秀。
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
struct Node{
int l, r;
mutable int v;
Node(int l, int r = 0, int v = 0) : l(l), r(r), v(v) {}
bool operator < (const Node &x) const {
return l < x.l;
}
};
set<Node> odt;
int n, m, seed, vmax, a[N];
auto split(int x) {
auto it = odt.lower_bound(Node(x));
if (it != odt.end() && it->l == x) return it;
--it;
if (it->r < x) return odt.end();
int l = it->l, r = it->r, v = it->v;
odt.erase(it);
odt.insert(Node(l, x - 1, v));
return odt.insert(Node(x, r, v)).first;
}
void assign(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
odt.erase(itl, itr);
odt.insert(Node(l, r, v));
}
void add(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; ++it)
it->v += v;
}
struct Rk{
int val, cnt;
bool operator < (const Rk &x) const { return val < x.val; }
Rk(int val, int cnt) : val(val), cnt(cnt) { }
};
int rk(int l, int r, int k) {
auto itr = split(r + 1), itl = split(l);
vector<Rk> q;
for (auto it = itl; it != itr; ++it)
q.push_back(Rk(it->v, it->r - it->l + 1));
sort(q.begin(), q.end());
for (auto i : q) {
if (i.cnt < k) k -= i.cnt;
else return i.val;
}
}
int qpow(int a, int b, int p) {
int res = 1;
a %= p;
for ( ; b; b >>= 1, a = a * a % p) if (b & 1) res = res * a % p;
return res;
}
int calP(int l, int r, int x, int y) {
auto itr = split(r + 1), itl = split(l);
int res = 0;
for (auto it = itl; it != itr; it++)
(res += qpow(it->v, x, y) * (it->r - it->l + 1) % y) %= y;
return res;
}
int rnd() {
int ret = seed;
seed = (seed * 7 + 13) % mod;
return ret;
}
signed main() {
cin >> n >> m >> seed >> vmax;
for (int i = 1; i <= n; ++i) {
a[i] = (rnd() % vmax) + 1;
odt.insert(Node(i, i, a[i]));
}
for (int i = 1; i <= m; ++i) {
int op, l, r, x, y;
op = (rnd() % 4) + 1;
l = (rnd() % n) + 1;
r = (rnd() % n) + 1;
if (l > r) swap(l, r);
if (op == 3) {
x = (rnd() % (r - l + 1)) + 1;
} else {
x = (rnd() % vmax) + 1;
}
if (op == 4) {
y = (rnd() % vmax) + 1;
}
if (op == 1) {
add(l, r, x);
} else if (op == 2) {
assign(l, r, x);
} else if (op == 3) {
cout << rk(l, r, x) << endl;
} else {
cout << calP(l, r, x, y) << endl;
}
}
return 0;
}