一、\(\text{珂朵莉树使用条件}\):
1.数据随机 2.有区间赋值操作
二、\(\text{核心思想}\)
用一个\(\text{set< node >}\)来存取每一个节点
节点的定义:
一段连续相等的区间可以放在一个节点里面,分别用\(l\),\(r\)表示左右边界,\(v\)表示它们的值,节点默认按照左端点排序,各个节点精确覆盖\(1\)~\(n\)
也就是说,最开始有不同的\(n\)个小区间,每次进行区间赋值,就可以将目标区间中的所有小区间合并为一个大区间,来减少复杂度
三、\(\text{各个函数}\)
下面以 CF896C \(Willem\), \(Chtholly\) \(and\) \(Seniorious\)作为例子
关键操作
1.“建树”,将每个点\(node\){ \(i\) , \(i\) , \(vi\) }加入\(set\)中
2.区间分裂:将一个区间按照指定的mid分割为
[ \(l\),\(mid\) ) , [ \(mid\) , \(r\) ],用\(lower\)_\(bound\)找到第一个左端点不小于\(mid\)的区间(那么目标区间要么是这一个,要么是前一个),删除目标区间,加入两个小区间
3.区间赋值:把该区间分离出来,用\(set\)自带函数\(erase\)删除整个区间(删除操作左闭右开),加入大区间
其他操作
4.区间加:分离出每个小区间,从左向右一个一个加
5.区间排名:分离出每个小区间,排序
6.区间幂次和:分离出每个小区间,直接求和,每个区间幂次和就是\((r-l+1)*v^k\)
\(Code\):
#include<bits/stdc++.h>
#define N 100005
#define MOD 1000000007
#define IT set<Node>::iterator
using namespace std;
typedef long long ll;
int n,m;
ll seed,vmax;
ll quickpow(ll a,ll b,ll mod=LLONG_MAX)//快速幂
{
ll ret=1%mod;
a%=mod;
while(b)
{
if(b&1) ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
namespace ODT
{
struct Node//ODT节点定义
{
int l,r;
mutable ll v;//要加mutable
Node(int l,int r=-1,ll v=0) : l(l),r(r),v(v) {}//构造函数
bool operator < (const Node &a)const//重载,set排序使用,默认按左端点排序
{
return l<a.l;
}
};
set<Node> s;
IT split(int mid)//拆分 ,返回拆分后右边区间的指针
{
IT it=s.lower_bound(Node(mid));//找到第一个左端点不小于mid的节点
if(it!=s.end()&&it->l==mid) return it;//如果这个节点左边就是mid则无需拆分
it--;//否则mid就在左边那个节点里面
int L=it->l,R=it->r;
ll v=it->v;//存储左右边界和值
s.erase(it);//删除大区间
s.insert(Node(L,mid-1,v));
return s.insert(Node(mid,R,v)).first;//加入小区间
}
void assign(int l,int r,ll v)//区间赋值,合并大坑
{
IT L=split(l),R=split(r+1);//拆分出[ l , r+1 )即 [ l , r ],R指向目标区间的下一个节点
s.erase(L,R);//删除这个范围内的所有区间
s.insert(Node(l,r,v));
}
void add(int l,int r,ll v)//区间加
{
IT L=split(l),R=split(r+1);
for(;L!=R;L++) L->v+=v;
}
ll rank(int l,int r,int k)//区间排名
{
IT L=split(l),R=split(r+1);
vector<pair<ll,int> > vc;//long long存值,int存区间长度
vc.clear();
for(;L!=R;L++) vc.push_back(pair<ll,int>(L->v,L->r-L->l+1));
sort(vc.begin(),vc.end());
for(vector<pair<ll,int> >::iterator it=vc.begin();it!=vc.end();++it)
{
k-=it->second;//如果减去这个区间之后排名不够了
if(k<=0) return it->first;//就说明第k小就在这个区间
}
return LLONG_MIN;
//否则说明k已经大于整个区间长度,无解,也可以在函数开头判断
}
ll qsum(int l,int r,ll v,ll mod)//区间幂次和
{
IT L=split(l),R=split(r+1);
ll ret=0;
for(;L!=R;L++) ret=(ret+(ll)(L->r-L->l+1)*quickpow(L->v,v,mod))%mod;
return ret;
}
}
ll rnd()
{
ll ret=seed;
seed=(seed*7+13)%MOD;
return ret;
}
ll a[N];
int main()
{
cin>>n>>m>>seed>>vmax;
for(int i=1;i<=n;++i)
{
a[i]=(rnd()%vmax)+1;
ODT::s.insert(ODT::Node(i,i,a[i]));//一个萝卜一个坑
}
ODT::s.insert(ODT::Node(n+1,n+1,0));//因为右端点到n时会用到n+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) ODT::add(l,r,ll(x));
if(op==2) ODT::assign(l,r,ll(x));
if(op==3) printf("%lld\n",ODT::rank(l,r,x));
if(op==4) printf("%lld\n",ODT::qsum(l,r,ll(x),ll(y)));
}
return 0;
}
原文:https://www.cnblogs.com/Chtholly/p/10393676.html