例题
利用可持久化线段树,从左至右依次扫一遍,因为我们只关心种类,故只需要保留最靠右的那一个数即可,其它的没用。查询时调用 所在的线段树。具体的,首先在下标 加 ,找到前一个值为 的下标 ,将其减 ,最后 ,这样就只保留最靠右的那一个数。查询时调用 的原因是:这颗线段树保留的是 最靠右的数,无论 取到什么, 都是区间种类数。
const int N=1e6+7;
int n,a[N],pos[N],root[N],tot;
struct Tree{
int sum[N*40],ls[N*40],rs[N*40];
void pushup(int p){sum[p]=sum[ls[p]]+sum[rs[p]];}
void modify(int q,int &p,int x,int k,int l=1,int r=n){
p=++tot;
ls[p]=ls[q];rs[p]=rs[q];sum[p]=sum[q];
if(l==r){sum[p]+=k;return;}
int mid=l+r>>1;
if(x<=mid)modify(ls[q],ls[p],x,k,l,mid);
else modify(rs[q],rs[p],x,k,mid+1,r);
pushup(p);
}
int query(int p,int ql,int qr,int l=1,int r=n){
if(ql<=l&&r<=qr)return sum[p];
int mid=l+r>>1,ans=0;
if(ql<=mid)ans+=query(ls[p],ql,qr,l,mid);
if(qr>mid)ans+=query(rs[p],ql,qr,mid+1,r);
return ans;
}
}tr;
int main(){
read(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++){
tr.modify(root[i-1],root[i],i,1);
if(pos[a[i]])tr.modify(root[i],root[i],pos[a[i]],-1);
pos[a[i]]=i;
}
int q;read(q);
while(q--){
int l,r;
read(l);read(r);
printf("%d\n",tr.query(root[r],l,r));
}
return 0;
}
树状数组做法类似可持久化线段树,只不过是将询问按 排序,然后从小到大处理,就不必可持久化。
莫队做法这题就是板题,可以在我的 这篇博客 里查看。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】