857
社区成员
这是我参加朝闻道知识分享大赛的第十篇文章。
珂朵莉树(Chtholly Tree)起源于CF896C,那道题要求我们实现一种数据结构,可以较快地实现:
区间加
区间赋值
求区间第k大值
求区间n次方和
适用场景
大部分涉及到区间赋值操作的问题,都可以用珂朵莉树,对于一些可以暴力骗分的比赛(比如
核心思想
把值相同的区间合并成一个结点保存在 set 里面。
下面给出模板题与模板代码(结尾处给出细节处理):
模板题:
C. Willem, Chtholly and Seniorious
应用:
封装珂朵莉树:
template<typename T>
struct ODT//珂朵莉树
{
struct Node
{
int l,r;
mutable T v;
Node(int _l,int _r = -1,T _v = 0):l(_l),r(_r),v(_v){}
bool operator<(const Node&o) const {return l < o.l;}
};
set<Node> S;
void insert(int l,int r,T v){//往set中插入结点
S.insert(Node(l,r,v));
}
auto split(int pos){//断开区间
auto it = S.lower_bound(Node(pos));
if(it != S.end() && it->l == pos) return it;
--it;
int l = it->l,r = it->r;
T v = it->v;
S.erase(it);
S.insert(Node(l,pos - 1,v));
return S.insert(Node(pos,r,v)).first;
}
void add(int l,int r,T v){ //区间加
for(auto end = split(r + 1),it = split(l);it != end;it++)
it->v += v;
}
void assign(int l,int r,T v){ //区间赋值
auto end = split(r + 1),begin = split(l);
S.erase(begin,end);
S.insert(Node(l,r,v));
}
T get_k(int l,int r,int k){ //区间第k大的数
vector<pair<T,int>> v;
for(auto end = split(r + 1),it = split(l);it != end;it++){
v.push_back({it->v,it->r - it->l + 1});
}
sort(v.begin(),v.end());
for(auto x : v){
k -= x.second;
if(k <= 0) return x.first;
}
return -1;
}
T sum_pow(int l,int r,int k,int p){ //区间[l,r]的k次幂之和
ll res = 0;
for(auto end = split(r + 1),it = split(l);it != end;it++){
res = (res + 1ll * (it->r - it->l + 1) * fpow(it->v,k,p) % p) % p;
}
return res;
}
};
模板题代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10,mod = 1e9 + 7;
int n,m,seed,vmax;
int a[N];
ll fpow(ll a,ll b,ll p)
{
a %= p;
ll res = 1;
while (b)
{
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
}
template<typename T>
struct ODT//珂朵莉树
{
struct Node
{
int l,r;
mutable T v;
Node(int _l,int _r = -1,T _v = 0):l(_l),r(_r),v(_v){}
bool operator<(const Node&o) const {return l < o.l;}
};
set<Node> S;
void insert(int l,int r,T v){//往set中插入结点
S.insert(Node(l,r,v));
}
auto split(int pos){//断开区间
auto it = S.lower_bound(Node(pos));
if(it != S.end() && it->l == pos) return it;
--it;
int l = it->l,r = it->r;
ll v = it->v;
S.erase(it);
S.insert(Node(l,pos - 1,v));
return S.insert(Node(pos,r,v)).first;
}
void add(int l,int r,T v){ //区间加
for(auto end = split(r + 1),it = split(l);it != end;it++)
it->v += v;
}
void assign(int l,int r,T v){ //区间赋值
auto end = split(r + 1),begin = split(l);
S.erase(begin,end);
S.insert(Node(l,r,v));
}
T get_k(int l,int r,int k){ //区间第k大的数
vector<pair<T,int>> v;
for(auto end = split(r + 1),it = split(l);it != end;it++){
v.push_back({it->v,it->r - it->l + 1});
}
sort(v.begin(),v.end());
for(auto x : v){
k -= x.second;
if(k <= 0) return x.first;
}
return -1;
}
T sum_pow(int l,int r,int k,int p){ //区间[l,r]的k次幂之和
ll res = 0;
for(auto end = split(r + 1),it = split(l);it != end;it++){
res = (res + 1ll * (it->r - it->l + 1) * fpow(it->v,k,p) % p) % p;
}
return res;
}
};
int rnd()
{
int ret = seed;
seed = (seed * 7ll + 13) % mod;
return ret;
}
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> m >> seed >> vmax;
ODT<ll> odt;
for(int i = 1;i <= n;i++)
{
a[i] = (rnd() % vmax) + 1;
odt.insert(i,i,a[i]);
}
int op,l,r,x,y;
while (m--)
{
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;
cout << odt.get_k(l,r,x) << "\n";
}
else
{
x = (rnd() % vmax) + 1;
if(op == 1) odt.add(l,r,x);
else if(op == 2) odt.assign(l,r,x);
else
{
y = (rnd() % vmax) + 1;
cout << odt.sum_pow(l,r,x,y) << "\n";
}
}
}
return 0;
}
注意: 往集合里插入结构体的时候,如果有缺省的构造函数,即如下:
Node(int _l,int _r = -1,ll _v = 0):l(_l),r(_r),v(_v){}
并且使用如下方式插入:
S.insert({i,i,a[i]});
则会出现问题!即它只接收我第一个参数,后面的全给忽视了!!!
因此一定要注意插入的时候用带上结构体。当然如果不定义构造函数,就可以直接用大括号插入。