演算法學習筆記(18):珂朵莉樹

Qerrj發表於2024-05-13

珂朵莉樹

這個名字我猜是來源於初次誕生這個演算法的題目->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;
}

相關文章