xsy2517. color
有 \(n\) 种颜色,第 \(i\) 种颜色的美丽值为 \(a_i\)。给定一根长度为 \(n\) 的颜色序列。一段区间的美丽值定义为出现的颜色的美丽值之和,如果一种颜色出现了多次,也只被计算一次。\(q\) 次操作,要支持单点修改颜色和区间询问美丽值,数据在线。
\(1\le n,q\le 10^5\)。
小清新主席树题。
考虑对区间中出现多次的颜色在最先出现的位置计算它,那么最先出现的位置满足上一次出现该颜色的位置在询问左端点 \(l\) 的左侧。于是可以把第 \(i\) 个位置抽象成坐标系上的一个点 \((\text{last}_{\text{col}_i}+1,i)\),询问就是 \((1\sim l,l\sim r)\) 矩形的和。容易用树状数组套主席树实现。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
struct Node{int ls,rs;ll sum;}tr[N*100];
int n,m,cnt,c[N],w[N],rt[N];ll ans;set<int>s[N];
inline int lst(int c,int p){
set<int>::iterator it=s[c].lower_bound(p);
return *(--it);
}
inline int nxt(int c,int p){
set<int>::iterator it=s[c].upper_bound(p);
return it==s[c].end()?-1:*it;
}
inline void update(int &x,int l,int r,int y,int v){
if(!x)x=++cnt;
if(l==r)return void(tr[x].sum+=v);
int mid=l+r>>1;
y<=mid?update(tr[x].ls,l,mid,y,v):update(tr[x].rs,mid+1,r,y,v);
tr[x].sum=tr[tr[x].ls].sum+tr[tr[x].rs].sum;
}
inline ll query(int x,int l,int r,int L,int R){
if(L<=l&&R>=r)return tr[x].sum;
int mid=l+r>>1;ll ans=0;
if(L<=mid)ans=query(tr[x].ls,l,mid,L,R);
if(R>mid)ans+=query(tr[x].rs,mid+1,r,L,R);
return ans;
}
inline void add(int x,int y,int v){for(;x<=n;x+=x&-x)update(rt[x],1,n,y,v);}
inline ll ask(int x,int l,int r){ll ans=0;for(;x;x&=x-1)ans+=query(rt[x],1,n,l,r);return ans;}
inline void modify(int p,int o){
int v=lst(c[p],p),u=nxt(c[p],p);
add(v+1,p,-w[c[p]]);
if(~u){
add(p+1,u,-w[c[p]]);
add(v+1,u,w[c[p]]);
}
s[c[p]].erase(p),c[p]=o;
s[o].insert(p);
v=lst(o,p),u=nxt(o,p);
if(~u){
add(v+1,u,-w[o]);
add(p+1,u,w[o]);
}
add(v+1,p,w[o]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",c+i),s[i].insert(0),s[c[i]].insert(i);
for(int i=1;i<=n;++i)scanf("%d",w+i);
for(int i=1;i<=n;++i)add(lst(c[i],i)+1,i,w[c[i]]);
while(m--){
int type;ll l,r;scanf("%d%lld%lld",&type,&l,&r);
if(type&1)modify(l^ans,r^ans);
else printf("%lld\n",ans=ask(l^ans,l^ans,r^ans));
}
return 0;
}