题解:P13983 数列分块入门 8
headless_piston · · 题解
珂朵莉树是很棒的数据结构!
观察题目,发现每次询问后必须进行一次区间推平操作,这启示我们用珂朵莉树做这道题。
代码实现非常简单,拆分出区间后直接暴力遍历颜色段并累加贡献即可。
接下来进行复杂度分析:
设势能函数
-
split:每次最多新增
1 个颜色段。 -
assign:两次 split 操作最多增加
2 个颜色段。设操作的区间[l,r] 中有k 个颜色段,那么操作将删去k 个区间然后增加1 个区间。
综上,对于一次 assign 操作有
那么,对于这
由于区间数量显然是正数,所以有:
回顾 assign 操作,设当前颜色段数为
根据前文,
空间复杂度显然是线性。
关于时间复杂度的感性理解:我们发现每次操作若
Code:
#include<cstdio>
#include<set>
using namespace std;
template<typename T>
inline void read(T &x);
template<typename T,typename...Args>
void read(T &x,Args &...args);
struct node{
int l,r,val;
bool operator<(const node &x)const{
return l<x.l;
}
};
int n;
set<node> odt;
auto split(int pos){
if(pos>n) return odt.end();
auto it=odt.lower_bound({pos});
if(it!=odt.end()&&it->l==pos)
return it;
it--;
int l=it->l,r=it->r,val=it->val;
odt.erase(it);
odt.insert({l,pos-1,val});
return odt.insert({pos,r,val}).first;
}
int assign(int l,int r,int c){
int res=0;
auto itr=split(r+1),itl=split(l);
for(auto it=itl;it!=itr;it++)
if(it->val==c) res+=it->r-it->l+1;
odt.erase(itl,itr);
odt.insert({l,r,c});
return res;
}
int main(){
read(n);
for(int i=1,a;i<=n;i++)
read(a),odt.insert({i,i,a});
for(int i=1,l,r,c;i<=n;i++){
read(l,r,c);
printf("%d\n",assign(l,r,c));
}
return 0;
}