珂朵莉树,一看就觉得很玄学
大概就是把若干个内部的值一样/等价/可以用少量的东西来表示出 的区间丢进(set) / 平衡树 里去,
然后和暴力一样维护题目要求的信息。
先来看一道题 CF896C
珂朵莉树的应用的重要条件:数据随机。
这样,操作序列中有期望 $ O(frac{m}{t}) $ 次 ((t)为操作种类数)
(Assign) (.)
而每次(Assign)之后(,)区间个数会大大减小(,)复杂度才有所保证。
(1) (Spilt(pos)) 分离操作
作用:将pos所在区间([L,R])分解为([L,pos-1])和([pos,R]).
#define IT set<Node>::iterator
IT spilt(int pos){
IT it = s.lower_bound(Node(pos));
if (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;
}
(2) (Assign(l,r,v)) 推平操作
作用:将(set)中的([l,r])分解出来并推平成一段区间。
void Assign(int l,int r,LL val = 0){
IT itl = spilt(l),itr = spilt(r+1);
s.erase(itl,itr);
s.insert(Node(l,r,val));
}
(3)、其他操作:都是暴力吧。。。 (() 实际上前两个操作也是暴力(雾) ())
void Add(int l,int r,int val){
IT itl = spilt(l),itr = spilt(r+1);
for (; itl != itr; ++itl) itl->v += val;
}
LL Kth(int l,int r,int k){
IT itl = spilt(l),itr = spilt(r+1);
vector<pair<LL,int> >rt; rt.clear();
for (; itl != itr; ++itl) rt.push_back(pair<LL,int>(itl->v,itl->r - itl->l + 1));
sort(rt.begin(),rt.end());
for (vector<pair<LL,int> >::iterator it = rt.begin(); it != rt.end(); ++it){
k -= it->second;
if (k <= 0) return it->first;
}
return -1;
}
LL Power(int l,int r,int val,int p){
IT itl = spilt(l),itr = spilt(r+1);
LL ret = 0;
for (; itl != itr; ++itl)
ret = (ret + (LL)(itl->r - itl->l + 1)*qpow(itl->v,val,p)%p) % p;
ret = (ret + p) % p;
return ret;
}
代码:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
int x = 0,f = 1; char c = getchar();
while (c != EOF && !isdigit(c)) {if (c == '-') f = -1;c = getchar();}
while (c != EOF && isdigit(c)) {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
inline void write(LL x){
int k = 0;char put[20];
if (!x) putchar('0');
if (x < 0) putchar('-'),x = -x;
while (x) put[++k] = x % 10 + '0',x /= 10;
while (k) putchar(put[k]),--k;
putchar('
');
}
namespace READ{
LL seed,ret;
inline int rnd(){
ret = seed;
seed = (seed * 7 + 13) % 1000000007;
return ret;
}
}
using namespace READ;
LL qpow(LL a,LL b,LL p){
a %= p; LL ret = 1;
while (b) { if (b&1) ret = ret*a%p; a = a*a%p,b>>=1; } return ret;
}
struct Node{
int l,r; mutable LL v;
Node (int ll,int rr = -1,LL val = 0){l = ll,r = rr,v = val;}
bool operator < (const Node x) const{ return l < x.l; }
};
set<Node>s;
#define IT set<Node>::iterator
IT spilt(int pos){
IT it = s.lower_bound(Node(pos));
if (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 Assign(int l,int r,LL val = 0){
IT itl = spilt(l),itr = spilt(r+1);
s.erase(itl,itr);
s.insert(Node(l,r,val));
}
void Add(int l,int r,int val){
IT itl = spilt(l),itr = spilt(r+1);
for (; itl != itr; ++itl) itl->v += val;
}
LL Kth(int l,int r,int k){
IT itl = spilt(l),itr = spilt(r+1);
vector<pair<LL,int> >rt; rt.clear();
for (; itl != itr; ++itl) rt.push_back(pair<LL,int>(itl->v,itl->r - itl->l + 1));
sort(rt.begin(),rt.end());
for (vector<pair<LL,int> >::iterator it = rt.begin(); it != rt.end(); ++it){
k -= it->second;
if (k <= 0) return it->first;
}
return -1;
}
LL Power(int l,int r,int val,int p){
IT itl = spilt(l),itr = spilt(r+1);
LL ret = 0;
for (; itl != itr; ++itl)
ret = (ret + (LL)(itl->r - itl->l + 1)*qpow(itl->v,val,p)%p) % p;
ret = (ret + p) % p;
return ret;
}
LL n,m,vmax,op,l,r,x,y;
int main(){
int i;
n = read(),m = read(),seed = read(),vmax = read();
s.insert(Node(0,0,0ll));
for (i = 1; i <= n; ++i) x = rnd() % vmax + 1,s.insert(Node(i,i,x));
s.insert(Node(n+1,n+1,0ll));
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;
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) write(Kth(l,r,x));
else write(Power(l,r,x,y));
}
return 0;
}
是不是很短啊qwq
然后还有,珂朵莉树还可以扩展到二维,所以可以把操作丢进(struct)里面去,然后把一维珂朵莉树当成(Node),用(set)维护一波即可。
以此类推可以扩展到任意高维。只要不(MLE),不(TLE)就没事了
好暴力啊!