专栏文章
题解:P13983 数列分块入门 8
P13983题解参与者 4已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @minzupcv
- 此快照首次捕获于
- 2025/12/02 11:03 3 个月前
- 此快照最后确认于
- 2025/12/02 11:03 3 个月前
珂朵莉树是很棒的数据结构!
观察题目,发现每次询问后必须进行一次区间推平操作,这启示我们用珂朵莉树做这道题。
代码实现非常简单,拆分出区间后直接暴力遍历颜色段并累加贡献即可。
观察题目,发现每次询问后必须进行一次区间推平操作,这启示我们用珂朵莉树做这道题。
代码实现非常简单,拆分出区间后直接暴力遍历颜色段并累加贡献即可。
接下来进行复杂度分析:
设势能函数 表示当前 ODT 中颜色段个数。显然初始势能 。
设势能函数 表示当前 ODT 中颜色段个数。显然初始势能 。
-
split:每次最多新增 个颜色段。
-
assign:两次 split 操作最多增加 个颜色段。设操作的区间 中有 个颜色段,那么操作将删去 个区间然后增加 个区间。
综上,对于一次 assign 操作有 。
那么,对于这 次操作,总体势能变化为:
由于区间数量显然是正数,所以有:
由于区间数量显然是正数,所以有:
回顾 assign 操作,设当前颜色段数为 ,两次 split 操作时间复杂度 ,遍历 个颜色段时间复杂度 ,删除 个颜色段时间复杂度 ,插入 个新颜色段复杂度 ,所以单次操作时间复杂度 。
根据前文,, 不可能大于 。所以 和 都是 的。所以总时间复杂度就是:
空间复杂度显然是线性。
关于时间复杂度的感性理解:我们发现每次操作若 很大,就会导致颜色段数急剧减少。而 极小时又不会产生过多的颜色段。所以给人的感觉就非常正确。
Code:
CPP#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;
}
相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...