P12194 [NOISG 2025 Prelim] Snacks 题解
废话时间

一开始看错了,以为是 $l_i$ 和 $r_i$ 是指下标,自顾自地说怎么这么简单。打完之后直接测试样例,发现错了,再返回看题面,发现维护的是值域,然后尖锐爆鸣。
正文
值域珂朵莉树,比较好想。
我们可以直接考虑珂朵莉树的每个节点表示的是什么意思。设 ${l,r,v}$ 表示对于 $i\in[l,r]$,$(\sum_{j=1}^n[i=a_j])=v$。那么题目中的操作就能直接转化为给区间内的数全都置为 $0$,然后再给节点 $v$ 加上区间内的数对应原数组中对应多少个数就行了。那么这就能直接抽象为一个区间推平和单点修改,可以直接使用珂朵莉树维护。
其实将操作看作区间推平和单点修改后,有很多种维护方法,比如线段树或者树状数组。但是有了区间推平的操作,感觉还是珂朵莉树好些一些。
代码
#include<bits/stdc++.h>
using std::set;
using std::cin;
using std::cout;
using std::ios;
#define int long long
struct node{
int l,r;
mutable int v;
node(int a=0,int b=-1,int c=0):l(a),r(b),v(c){}
bool operator<(const node &o)const{
return l<o.l;
}
};
set<node> s;
int ans,l,r,n,q,x;
auto split(int pos){
auto it=s.lower_bound(node(pos));
if(it!=s.end()&&it->l==pos) return it;
--it;
int l=it->l,r=it->r,v=it->v;
s.erase(it);
s.insert(node(l,pos-1,v));
return s.insert(node(pos,r,v)).first;
}
void upd(int d,int k){
auto ir=split(d+1),il=split(d);
for(auto it=il;it!=ir;++it)
it->v+=k;
ans+=d*k;
}
int asg(int l,int r,int k){
int sum=0;
auto ir=split(r+1),il=split(l);
for(auto it=il;it!=ir;++it){
ans-=(it->r+it->l)*(it->r-it->l+1)/2*it->v;
sum+=(it->r-it->l+1)*it->v;
}
s.erase(il,ir);
s.insert(node(l,r,0));
upd(k,sum);
return ans;
}
void solve(){
cin>>n>>q;
s.insert(node(0,1e9,0));
for(int i=1;i<=n;i++)
cin>>x,upd(x,1);
cout<<ans<<"\n";
while(q--){
cin>>l>>r>>x;
cout<<asg(l,r,x)<<"\n";
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
solve();
return 0;
}
感谢阅读。

浙公网安备 33010602011771号