珂朵莉树学习笔记

珂朵莉树又叫老司机树。

思路十分简单,众所周知,越暴力的数据结构能干的东西也就越多。

你看隔壁分块啥都能干

珂朵莉树最重要的有 \(2\) 个操作:

  • Spilt 操作,将区间分裂

  • Cover 操作,将区间覆盖

珂朵莉能用的前提是保证 Cover 操作的数量,所以你想卡 ODT 只需要把 Cover 的数量减少就行。

\(Spilt\) 操作:

分裂操作 将区间 \([l, r]\) 分为 \([l, pos - 1]\)\([pos, r]\) 最后返回的是 \([pos, r]\) 这个区间的迭代器。

首先我们要找到 pos 这个点的位置才能确定分裂哪个操作。

如果我们找到包含 pos 的区间的左端点正好是 pos,那直接返回这个区间。

如果不是,那么 pos 就在上一个区间,先将迭代器向前进一步,然后将原先的区间删除,将新的两个区间加入。

	It Spilt(int pos) {
		/*分裂操作 将区间 [l, r] 分为 [l, pos - 1] 和 [pos, r] 最后返回的是 [pos, r] 这个区间的迭代器*/
		It it = s.lower_bound(node(pos)); // 二分找到第一个大于等于 pos 的区间
		if(it != s.end() && it->l == pos) return it; // 如果区间的左端点正好是 pos 直接返迭代器
		--it; // 如果不是pos 就在前一个区间
		int L = it -> l, R = it -> r, V = it -> v; 
		s.erase(it); // 将原先的区间删掉
		s.insert(node(L, pos - 1, V)); 
		return s.insert(node(pos, R, V)).first;
		/*s.insert 返回的是一个 pair 类型, 返回的就是 [pos, r] 这个区间的迭代器*/
	}

\(Cover\) 操作:

覆盖操作。

代码虽短,但这是时间复杂度的保证。

先把 \([l, r]\) 这一段区间分出来,里面可能会包括很多区间,将这些区间都删除,然后加入一个新的维护的区间。

每一次操作都能将区间个数减少 \(\frac{2}{3}\)

void Cover(int l, int r, int Val) {
		It it2 = Spilt(r + 1), it1 = Spilt(l);
		s.erase(it1, it2); s.insert(node(l, r, Val));
		// 把分开的区间中间的全部删掉,然后插入新的
		// 这是ODT复杂度的保证 每次能有效的减少 set 内的个数
	}

来个例题代码:

CF896C

/*
    Work by: TLE_Automation
*/
#include<set>
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define int long long 
using namespace std;

//const int N = 1e6 + 10;
//const int MAXN = 2e5 + 10;

inline char readchar() {
	static char buf[100000], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int read() {
#define readchar getchar
	int res = 0, f = 0;char ch = readchar();
	for(; !isdigit(ch); ch = readchar()) if(ch == '-') f = 1;
	for(; isdigit(ch); ch = readchar()) res = (res << 1) + (res << 3) + (ch ^ '0');
	return f ? -res : res;
}

inline void print(int x) {
	if (x < 0 ) putchar('-'), x = -x;
	if (x > 9 ) print(x / 10);
	putchar(x % 10 + '0');
}

int Qpow(int a, int b, int mod) {
	int base = a % mod, res = 1;
	while(b) {
		if(b & 1) res = (res * base) % mod;
		base = (base * base) % mod, b >>= 1;
	}return res % mod;
}

int n, m, seed, vmax;
const int Mod = 1e9 + 7;

int Rand() {
	int ret = seed;
	seed = (seed * 7 + 13) % Mod;
	return ret;
}

namespace Chtholly_Tree {
	struct node {
		int l, r;
		mutable int v; // mutable 是可变的意思,不加后面调用迭代器会CE
		node (int L, int  R = -1, int V = 0) : l(L), r(R), v(V) {} // 初始化
		bool operator < (const node &x) const { return l < x.l; } // set 里用结构体要重载小于号
	};
#define It set<node>::iterator 
	set <node> s;
	It Spilt(int pos) {
		/*
			分裂操作 将区间分为 [l, pos - 1] 和 [pos, r] 
			最后返回的是 [pos, r] 这个区间的迭代器
		*/
		It it = s.lower_bound(node(pos)); // 二分找到第一个大于等于 pos 的区间
		if(it != s.end() && it->l == pos) return it; // 如果区间的左端点正好是 pos 直接返迭代器
		--it; // 如果不是pos 就在前一个区间
		int L = it -> l, R = it -> r, V = it -> v; 
		s.erase(it); // 将原先的区间删掉
		s.insert(node(L, pos - 1, V)); 
		return s.insert(node(pos, R, V)).first;
		/*
			s.insert 返回的是一个 pair 类型
			返回的就是 [pos, r] 这个区间的迭代器
		*/
	}
	void Add(int l, int r, int Val) {
		It it2 = Spilt(r + 1), it1 = Spilt(l); 
		for(; it1 != it2; ++it1) it1 -> v += Val; // 暴力加
	}
	void Cover(int l, int r, int Val) {
		It it2 = Spilt(r + 1), it1 = Spilt(l);
		s.erase(it1, it2); s.insert(node(l, r, Val));
		// 把分开的区间中间的全部删掉,然后插入新的
		// 这是ODT复杂度的保证 每次能有效的建设 set 内的个数
	}
	int Rank(int l, int r, int k) {
		/*
			将这部分区间全放到一个 vector 容器里
			然后暴力找第 k 个
		*/
		vector <pair<int, int > > vec;
		It it2 = Spilt(r + 1), it1 = Spilt(l);
		vec.clear();
		for(; it1 != it2; ++it1)
			vec.push_back(pair <int, int> (it1 -> v, it1 -> r - it1 -> l + 1));
		sort(vec.begin(), vec.end());
		for(vector <pair <int, int> >::iterator it = vec.begin(); it != vec.end(); ++it) {
			k -= it -> second;
			if(k <= 0) return it -> first;
		}
	}
	int Query(int l, int r, int p, int mod) {
		It it2 = Spilt(r + 1), it1 = Spilt(l);
		int res = 0;
		// 暴力求幂次和
		for(; it1 != it2; ++it1) 
			res = (res + (it1 -> r - it1 -> l + 1) *  Qpow(it1 -> v, p, mod) ) % mod;
		return res;
	}
}
using namespace Chtholly_Tree;

signed main() {
	n = read(), m = read(), seed = read(), vmax = read();   
	for(int i = 1; i <= n; i++) {
		int x = (Rand() % vmax)+ 1;
		s.insert(node(i, i, x));
	}
	s.insert(node(n + 1, n + 1, 0));
	for(int i = 1, opt, l, r, x, y; i <= m; ++i) {
		opt = (Rand() % 4) + 1, l = (Rand() % n) + 1, r = (Rand() % n) + 1;
		if(l > r) swap(l, r);
		if(opt == 3) x = (Rand() % (r - l + 1)) + 1;
		else x = (Rand() % vmax) + 1;
		if(opt == 4) y = (Rand() % vmax) + 1;
		if(opt == 1) {
			Add(l, r, x);
		} else if(opt == 2) {
			Cover(l, r, x);
		} else if(opt == 3) {
			print(Rank(l, r, x)), putchar('\n');
		} else {
			print(Query(l, r, x, y)), putchar('\n');
		}
	}
	return 0;
}
posted @ 2022-05-20 20:05  TLE_Automation  阅读(41)  评论(1编辑  收藏  举报