[数据结构] 珂朵莉树

介绍

珂朵莉树,学名珂朵莉树,又学名老司机树( 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,pos1,vpos,r,v 两个区间即可;

一个技巧:setinsert 函数有一个返回值,是 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 @   Peppa_Even_Pig  阅读(56)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示