颜色段均摊和 CDQ 分治
EnofTaiPeople · · 题解
颜色段均摊有两层含义,都是建立在存在区间赋值(推平)操作的基础上。
- 随机数据:任意时刻的颜色段个数期望
O(\log_2n) ; - 非随机数据(本题):每次推平时访问的颜色段个数为均摊
O(n) 。
考虑本题有区间推平操作,所以使用珂朵莉树,然而我们发现数据不随机,所以暴力上珂朵莉树是
因为要求的是不同颜色个数,我们可以用只算第一次出现的方法来计算,设
这个数组有什么用?发现
如何维护这个数组?因为每次推平操作时,每一个被删除的颜色段都会有 set 维护每种颜色的当前存在段,以便删除段时更改后继左端点的
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
using ll=long long;
int n,QT,a[N],qt,rq,pr[N],mp[N],mt;
struct Q{int l,r,d,p;}q[N*6],q2[N*6];
struct odt{
int l,r;mutable int d;
bool operator<(const odt &z)
const{return l<z.l;}
};
struct rag{
int l,r;
bool operator<(const rag &z)
const{return r<z.r;}
};
void addp(int x,int d){q[++rq]={pr[x],x,d};}
void cgpr(int x,int y){
if(pr[x]!=y)addp(x,-1),pr[x]=y,addp(x,1);
}
struct RGdt:set<rag>{
void add(int l,int r){
auto it=lower_bound({0,r});
if(it!=end())cgpr(it->l,r);
if(it!=begin())--it,cgpr(l,it->r);
else cgpr(l,0);
insert({l,r});
}void del(int l,int r){
erase({l,r});
auto it=lower_bound({0,r});
if(it!=end()){
if(it!=begin()){
auto at=it;--at;
cgpr(it->l,at->r);
}else cgpr(it->l,0);
}
}
}lt[N];
struct Odt:set<odt>{
set<odt>::iterator split(int r){
if(r>n)return end();
auto it=lower_bound({r});
if(it!=end()&&it->l==r)return it;
--it;int L=it->l,R=it->r,D=it->d;
lt[D].erase({L,R}),lt[D].insert({L,r-1});
lt[D].insert({r,R});
erase(it),insert({L,r-1,D});
auto res=insert({r,R,D}).first;
return res;
}
}ADT;
ll ct[N],ans[N];
void solve(int l,int r){
if(l>=r)return;
int i,x,y,md=l+r>>1;
solve(l,md),solve(md+1,r);
for(x=md+1,y=l;x<=r;++x){
while(y<=md&&q[y].l<=q[x].l){
if(!q[y].p)for(i=q[y].r;i<=n;i+=i&-i)ct[i]+=q[y].d;++y;
}if(q[x].p)for(i=q[x].r;i;i-=i&-i)ans[q[x].p]+=q[x].d*ct[i];
}
for(x=l;x<y;++x)
if(!q[x].p)for(i=q[x].r;i<=n;i+=i&-i)ct[i]-=q[x].d;
merge(q+l,q+md+1,q+md+1,q+r+1,q2+l,[&](Q x,Q y){
return x.l<y.l;
});for(i=l;i<=r;++i)q[i]=q2[i];
}
int main(){
ios::sync_with_stdio(false);
int i,j,A,l,r,d,qi;cin>>n>>QT;
for(i=1;i<=n;++i)cin>>a[i],mp[++mt]=a[i];
for(qi=1;qi<=QT;++qi){
cin>>q2[qi].p>>q2[qi].l>>q2[qi].r;
if(q2[qi].p==1){
cin>>q2[qi].d;
mp[++mt]=q2[qi].d;
}
}
sort(mp+1,mp+mt+1),unique(mp+1,mp+mt+1)-mp-1;
for(i=1;i<=n;++i)a[i]=lower_bound(mp+1,mp+mt+1,a[i])-mp;
for(qi=1;qi<=QT;++qi)
if(q2[qi].p==1)
q2[qi].d=lower_bound(mp+1,mp+mt+1,q2[qi].d)-mp;
for(i=1;i<=n;++i){
ADT.insert({i,i,a[i]});
if(lt[a[i]].size()){
auto it=lt[a[i]].end();--it;
pr[i]=it->r;
}lt[a[i]].insert({i,i});
addp(i,1);
}
for(qi=1;qi<=QT;++qi){
A=q2[qi].p,l=q2[qi].l,r=q2[qi].r,d=q2[qi].d;
if(A==1){
auto R=ADT.split(r+1),L=ADT.split(l);
auto it=L;lt[it->d].del(it->l,it->r);
for(++it;it!=R;++it)cgpr(it->l,it->l-1),lt[it->d].del(it->l,it->r);
ADT.erase(L,R);
ADT.insert({l,r,d});
lt[d].add(l,r);
}else{
++qt,q[++rq]={l-1,r,1,qt};
q[++rq]={l-1,l-1,-1,qt};
}
}solve(1,rq);
for(i=1;i<=qt;++i)
printf("%lld\n",ans[i]);
return 0;
}