珂朵莉树的树状数组实现方法
珂朵莉树概述
在一部分数据结构问题中,对于多个连续相同的值我们可以放在一起处理,这就是珂朵莉树的基本思想。我们将序列分成若干个三元组
对于一部分问题,珂朵莉树的复杂度是错误的,但是在不断覆盖中,颜色段的数量会锐减,使得珂朵莉树可以高效解决。
对于另一部分问题,我们在查询的同时将其覆盖,意味着每次遍历都代表一次颜色段合并,由于合并总次数往往不会太多,均摊下来复杂度则是正确的。我们将给出一种这种问题的更好的珂朵莉树实现方式。
珂朵莉树更好的实现方法(树状数组)
参考 oiwiki 中给出的 set 实现方法与链表实现方法,我们可以知道珂朵莉本质上是链表结构,所以当确定头尾所在的颜色段之后就可以用前驱后继高效遍历。
但是如何快速寻找一个点所在的颜色段呢?
我们考虑颜色段是什么样子的:
颜色段完全覆盖整个序列且互不相交。
我们设置一个标记数组
我们不妨对于每个颜色段
考虑查询的都是前缀,所以树状数组维护即可。
测试效果
P13983数列分块入门 8
#include<bits/stdc++.h>
#define int unsigned int
using namespace std;
const int N=3e5+5;
int l[N],r[N],c[N],tree[N],n;
void read(int &x){
int flag=1;
char c=getchar_unlocked();
while(c<'0'||c>'9'){
if(c=='-'){
flag=-1;
}
c=getchar_unlocked();
}
x=0;
while('0'<=c&&c<='9'){
x*=10,x+=c-'0';
c=getchar_unlocked();
}
x*=flag;
}
void write(int &x){
if(!x){
putchar_unlocked('0');
return ;
}
int cnt=0,val[22];
while(x){
val[++cnt]=x%10;
x/=10;
}
while(cnt){
putchar_unlocked('0'+val[cnt--]);
}
}
void upd(int x){
for(int i=l[x];i<=n;i+=(i&-i))tree[i]^=l[x];
for(int i=r[x]+1;i<=n;i+=(i&-i))tree[i]^=l[x];
}
int query(int x){
int ans=0;for(int i=x;i;i-=(i&-i))ans^=tree[i];
return ans;
}
void split(int x){
if(!x)return ;
int id=query(x);
if(x==r[id])return ;
upd(id);
c[x+1]=c[id];l[x+1]=x+1;r[x+1]=r[id];
r[id]=x;
upd(x+1);upd(id);
}
signed main(){
read(n);
for(int i=1;i<=n;i++){
int x;read(x);l[i]=r[i]=i;
c[i]=x;
upd(i);
}
for(int i=1;i<=n;i++){
int L,R,C;
read(L);read(R);read(C);
int ans=0;
split(L-1);split(R);
for(int j=L;j<=R;j=r[j]+1){
ans+=(r[j]-l[j]+1)*(c[j]==C);upd(j);
}
l[L]=L;r[L]=R;c[L]=C;upd(L);
write(ans);putchar_unlocked('\n');
}
return 0;
}
目前题目最优解 rk1。