珂朵莉树
前言
因为隔壁大佬自学了珂朵莉树,所以我心血来潮,也学了一下
定义
用自己的话谈一谈珂朵莉树:
珂朵莉树,简称\(ODT(Old\space Driver\space Tree)\),即老司机树
用\(set\)维护,每个区间为一个元素,存放在\(set\)里(那它怎么叫树呢)
每个区间的元素数值相同
struct node
{
int l,r;
mutable LL val;
node(int l1,int r1 = -1,LL val1 = 0){
l = l1;
r = r1;
val = val1;
}
bool operator < (const node &px)const {
return l < px.l;
}
};
小笔记:\(mutable\) 的中文意思是可变的,在迭代器中,结构体中的值会被当成常量,即有\(const\)的前缀,本来是不能被改变的,但是加了\(mutable\) 就可以被改变了
适用于区间赋值和其它操作且题目数据随机的情况
其实就是大暴力
正题
一、split函数
这个函数是珂朵莉树的核心
通过百度翻译可知,这个函数的意思是分裂
你想一想,如果现在有一个区间是\(l\)$r$,假设我们要查找的是$l$\(x(l\le x< r)\),没有现成的区间,我们就可以把这个区间删除,分成\(l\)$x$和$(x+1)$\(r\)
于是就有了现成的区间
或者说我们负责保证一定有一个端点\(x\)存在
那么我们就可以这么实现:
#define IT set<node>::iterator
IT split(int pos)//分裂
{
IT it = s.lower_bound(node(pos));
if(it != s.end() && it->l == pos)
return it;
--it;
int l = it->l,r = it->r;
LL dz = it->val;
s.erase(it);
s.insert(node(l,pos-1,dz));
return s.insert(node(pos,r,dz)).first;
//JZMの小科普:set::insert返回pair<iterator,bool> ,分别为新加入元素迭代器、是否加入成功。
}
至于为什么要返回迭代器
那是因为为了方便我们之后的操作
二、merge函数
这个函数是珂朵莉树的灵魂
可以快速减少区间的个数,降低运行时间
我们只需要删除原来的\(l\)~\(r\),再重新加入就行了
删除的时候可以用内置函数\(erase\)实现
void merg(int l,int r,LL val)//合并l~r
{
IT R = split(r+1),L = split(l);
s.erase(L,R);
//JZMの小科普:set::erase(iterator,iterator)可以左闭右开地删除元素。
s.insert(node(l,r,val));
}
这里要注意的是\(merge\)是关键字,所以我用了\(merg\)代替
三、其它操作
暴力才是本质
这里只列举一个操作:区间加
void Add(int l,int r,LL val)//暴力加就完事了
{
IT R = split(r+1),L = split(l);
for(;L != R;++ L)
L->val += val;
}
真的是暴力啊!!!
反正其它的都是暴力,听了这么多,你自己一定能根据题目随机应变码出来的
练习
Physical Education Lessons(洛谷)
Physical Education Lessons(CodeForces)
代码
板题代码
//12252024832524
#include <set>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#define IT set<node>::iterator
using namespace std;
typedef long long LL;
const int MAXN = 10005;
int n,m,vmax;
LL seed;
struct node
{
int l,r;
mutable LL val;
node(int l1,int r1 = -1,LL val1 = 0){
l = l1;
r = r1;
val = val1;
}
bool operator < (const node &px)const {
return l < px.l;
}
};
set<node> s;
int Read()
{
int x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
void Put(LL x)
{
if(x > 9) Put(x/10);
putchar(x%10^48);
}
template <typename T>T Max(T x,T y){return x > y ? x : y;}
template <typename T>T Min(T x,T y){return x < y ? x : y;}
template <typename T>T Abs(T x){return x < 0 ? -x : x;}
LL rnd()
{
LL ret = seed;
seed = (seed * 7 + 13) % 1000000007;
return ret;
}
LL qpow(LL x,int y,int MOD)
{
x %= MOD;
LL ret = 1;
while(y)
{
if(y & 1)
ret = ret * x % MOD;
x = x * x % MOD;
y >>= 1;
}
return ret;
}
IT split(int pos)//分裂
{
IT it = s.lower_bound(node(pos));
if(it != s.end() && it->l == pos)
return it;
--it;
int l = it->l,r = it->r;
LL dz = it->val;
s.erase(it);
s.insert(node(l,pos-1,dz));
return s.insert(node(pos,r,dz)).first;
//JZMの小科普:set::insert返回pair<iterator,bool> ,分别为新加入元素迭代器、是否加入成功。
}
void merge(int l,int r,LL val)//合并l~r
{
IT R = split(r+1),L = split(l);
s.erase(L,R);
//JZMの小科普:set::erase(iterator,iterator)可以左闭右开地删除元素。
s.insert(node(l,r,val));
}
void Add(int l,int r,LL val)//暴力加就完事了
{
IT R = split(r+1),L = split(l);
for(;L != R;++ L)
L->val += val;
}
LL rankk(int l,int r,int k)//暴力跑就完事了
{
vector<pair<LL,int> > v;
IT R = split(r+1),L = split(l);
for(;L != R;++ L)
v.push_back(make_pair(L->val,L->r - L->l + 1));
sort(v.begin(),v.end());
vector<pair<LL,int> >::iterator it;
for(it = v.begin();it != v.end();++ it)
{
k -= it->second;
if(k <= 0)
return it->first;
}
}
LL mi(int l,int r,int x,int MOD)
{
IT R = split(r+1),L = split(l);
LL ret = 0;
for(;L != R;++ L)
ret = (ret + qpow(L->val,x,MOD) * (L->r - L->l + 1) % MOD) % MOD;
return ret;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read();
m = Read();
seed = Read();
vmax = Read();
for(int i = 1;i <= n;++ i)
s.insert(node(i,i,(rnd() % vmax) + 1));
for(int i = 1;i <= m;++ i)
{
int op = (rnd() % 4) + 1;
int l = (rnd() % n) + 1;
int r = (rnd() % n) + 1;
int x,y;
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)
merg(l,r,x);
else if(op == 3)
Put(rankk(l,r,x));
else if(op == 4)
Put(mi(l,r,x,y));
if(op >= 3)
putchar('\n');
}
return 0;
}
Physical Education Lessons代码
//12252024832524
#include <set>
#include <cstdio>
#include <algorithm>
#define IT set<node>::iterator
using namespace std;
typedef long long LL;
const int MAXN = 10005;
int n,tot;
int Read()
{
int x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
void Put1(int x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
void Put(int x)
{
if(x < 0) putchar('-'),x = -x;
Put1(x);
}
template <typename T>T Max(T x,T y){return x > y ? x : y;}
template <typename T>T Min(T x,T y){return x < y ? x : y;}
template <typename T>T Abs(T x){return x < 0 ? -x : x;}
struct node
{
int l,r;
mutable bool val;
node(){}
node(int l1,int r1 = -1,bool val1 = 0){
l = l1;
r = r1;
val = val1;
}
bool operator < (const node &px)const {
return l < px.l;
}
};
set<node> s;
IT split(int x)
{
IT it = s.lower_bound(node(x));
if(it != s.end() && it->l == x)
return it;
it--;
int l = it->l,r = it->r;
bool dz = it->val;
s.erase(it);
s.insert(node(l,x-1,dz));
return s.insert(node(x,r,dz)).first;
}
void merg(int l,int r,bool val)
{
IT R = split(r+1),L = split(l);
for(IT it = L;it != R;++ it)
{
if(val == 1 && it->val == 0) tot += (it->r - it-> l + 1);
if(val == 0 && it->val == 1) tot -= (it->r - it-> l + 1);
}
s.erase(L,R);
s.insert(node(l,r,val));
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
tot = n = Read();
s.insert(node(1,n,1));
for(int Q = Read(); Q ;-- Q)
{
int l = Read(),r = Read(),opt = Read();
merg(l,r,opt-1);
Put(tot);
putchar('\n');
}
return 0;
}
By The Way
本博客借鉴了两位大佬的博客,分别是:
一号大佬JZM JZM orz or2 orz
二号不知名大佬 orz or2 orz