[数据结构] 珂朵莉树

介绍

珂朵莉树,学名珂朵莉树,又学名老司机树( $ ODT $ ),常用来解决“区间推平”(把区间改为某一个数)等的操作,只适用于随机数据,可以定向卡掉;

同机房dalao说:

如果只有区间推平的操作,就不用保证数据随机。 ——int_R

其实这玩意就是暴力,没啥可说的,分块都比不上她暴力;

但人家毕竟是,所以对于一些题还是有用的;

对于复杂度证明,详见https://www.luogu.com.cn/blog/blaze/solution-cf896c

实现

只要你会 $ C++ \ STL $ 里的 $ set $,搞定她完全不是问题;

当然,你还需要一些基本的指针操作;

主要思想:维护一段序列的所有连续的子区间(这里的“连续”指的是值连续);

对于 $ set $ 里的每一个元素,其为三元组,记录 $ l, r, v $,分别代表本段连续区间的左端点,右端点,值;

当然,$ set $ 中的元素是按 $ l $ 升序排列的;

声明:

struct sss{
	long long l, r;
	mutable long long v;
	bool operator <(const sss &A) const {
		return l < A.l;
	}
};

注:$ mutable $ 其实是和 $ const $ 相对应的,使用 $ mutable $ 可以使变量 $ v $ 在一个常量结构体下仍然可以改变;

$ mutable $ 具体需要在区间加等操作(见下文的的操作三)中用;

操作一 spilt

分裂操作,即指定一个 $ pos $,将整个序列分成两部分;

显然,最多只会对一个子区间进行操作;

借助 $ set $ 中的 $ lower \ bound $ 函数,我们可以快速查找出 $ pos $ 在哪个子区间里,此时会有这么几种情况:

  1. 此区间的 $ l = pos $;

直接返回此区间即可;

  1. 此区间的上一个区间的 $ r < pos $;

我们可以发现,此时 $ pos $ 太大了,已经超出了区间长度,所以直接返回迭代器即可;

  1. $ pos $ 在此区间的上一个区间中;

把此区间的上一个区间拆分成 $ l, pos - 1, v $ 和 $ pos, r, v $ 两个区间即可;

一个技巧:$ set $ 的 $ insert $ 函数有一个返回值,是 $ pair $ 类型的,$ first $ 返回插入元素的迭代器,$ second $ 返回这个元素是否被插入(因为 $ set $ 不可重);

set<sss>::iterator spilt(long long pos) {
	set<sss>::iterator it = s.lower_bound(sss{pos, 0, 0});
	if (it != s.end() && it -> l == pos) return it;
	it--;
	if (it -> r < pos) return s.end();
	long long l = it -> l;
	long long r = it -> r;
	long long v = it -> v;
	s.erase(it);
	s.insert(sss{l, pos - 1, v});
	return s.insert(sss{pos, r, v}).first;
}

注:$ it -> l $ 等价于 $ *it.l $;

操作二 assign

区间推平操作,设要修改的区间为 $ [l, r] $,我们可以先 $ spilt(r + 1) $,再 $ spilt(l) $,(顺序不能颠倒,不然会 $ RE $),最后删除这两步操作所对应的迭代器之间的所有元素(子区间),并插入要修改的元素(区间)即可;

void assign(long long l, long long r, long long x) {
	set<sss>::iterator rit = spilt(r + 1), lit = spilt(l);
	s.erase(lit, rit);
	s.insert(sss{l, r, x});
}

注:$ s.erase(a, b) $ 指的是删除 $ [a, b) $ 之间的元素;

操作三 其它暴力操作

几乎所有区间上的操作,只要你会暴力,就能用她做;

对于一段要操作的区间 $ [l, r] $,直接暴力遍历其中所有的元素,在进行操作即可;

例题:CodeForces 896C

对于第三个操作,发现用线段树不太好做,又发现数据随机,所以直接珂朵莉树搞它就行;

点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
long long n, m, seed, vmax;
long long a[1000005];
struct sss{
	long long l, r;
	mutable long long v;
	bool operator <(const sss &A) const {
		return l < A.l;
	}
};
set<sss> s;
set<sss>::iterator spilt(long long pos) {
	set<sss>::iterator it = s.lower_bound(sss{pos, 0, 0});
	if (it != s.end() && it -> l == pos) return it;
	it--;
	if (it -> r < pos) return s.end();
	long long l = it -> l;
	long long r = it -> r;
	long long v = it -> v;
	s.erase(it);
	s.insert(sss{l, pos - 1, v});
	return s.insert(sss{pos, r, v}).first;
}
void assign(long long l, long long r, long long x) {
	set<sss>::iterator rit = spilt(r + 1), lit = spilt(l);
	s.erase(lit, rit);
	s.insert(sss{l, r, x});
}
long long rnd() {
	long long ret = seed;
	seed = (seed * 7 + 13) % 1000000007;
	return ret;
}
void add(long long l, long long r, long long d) {
	set<sss>::iterator rit = spilt(r + 1), lit = spilt(l);
	for (set<sss>::iterator it = lit; it != rit; it++) {
		it -> v += d; //mutable用处
	}
}
vector<pair<long long, long long> > v;
long long ask_kth(long long l, long long r, long long k) {
	set<sss>::iterator rit = spilt(r + 1), lit = spilt(l);
	v.clear();
	for (set<sss>::iterator it = lit; it != rit; it++) {
		v.push_back({it -> v, it -> r - it -> l + 1});
	}
	sort(v.begin(), v.end());
	long long o = 0;
	while(1) {
		k -= v[o].second;
		if (k <= 0) return v[o].first;
		o++;
	}
}
long long ksm(long long a, long long b, long long c) {
	long long ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % c;
		a = a * a % c;
		b >>= 1;
	}
	return ans;
}
long long mo(long long l, long long r, long long x, long long y) {
	set<sss>::iterator rit = spilt(r + 1), lit = spilt(l);
	long long ans = 0;
	for (set<sss>::iterator it = lit; it != rit; it++) {
		ans = (ans + ksm(it -> v % y, x, y) * (it -> r - it -> l + 1) % y) % y;
	}
	return ans;
}
int main() {
	cin >> n >> m >> seed >> vmax;
	for (long long i = 1; i <= n; i++) {
		a[i] = (rnd() % vmax) + 1;
		s.insert(sss{i, i, a[i]});
	}
    for (long long i = 1; i <= m; ++i) {
        long long 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 << ask_kth(l, r, x) << endl;
        } else if (op == 4) {
        	cout << mo(l, r, x, y) << endl;
        }
    }
	return 0;
}
posted @ 2024-07-29 20:43  Peppa_Even_Pig  阅读(66)  评论(2)    收藏  举报