题面
解析
这题本来莫队可以过的。
然而,对于某些加强的数据,莫队就得吸氧了。。
所以,本题解还将介绍另一种算法——树状数组。
首先,莫队就不用讲了吧(毕竟只是板子)。
那么,开始进入正题(似乎有点啰嗦)。
我们先将每个询问存下来(还是离线处理),
然后再以右端点为关键字从小到大排序。
然后,对于1~n中的每个点r,
记录下区间1~r中每种颜色最右边的位置,
即在树状数组中将每种颜色最右边的位置设为1。
显然,如果询问q的右端点正好为r的话,
那么对于q的左端点l,
这种记录方式是最优的。
因为如果不是记录的最右边的点的话,就有可能忽略。
所以,先把询问按右端点排序,
再O(n)扫一遍,
并用树状数组计算前缀和,统计答案就行了
具体看代码吧:
#include<bits/stdc++.h> #define lowbit(a) (a&(-a)) using namespace std; inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return f*sum; } const int MAXN=500001; struct node{ int l,r,id; }q[MAXN]; int n,m; int ans[MAXN],t[MAXN],a[MAXN]; int pla[MAXN*2]; bool cmp(node a,node b){ return a.r<b.r; } inline void add(int x,int k){ for(int i=x;i<=n;i+=lowbit(i)){ t[i]+=k; } } inline int ask(int x){ int ret=0; for(int i=x;i;i-=lowbit(i)){ ret+=t[i]; } return ret; } int main(){ n=read(); for(int i=1;i<=n;i++) a[i]=read(); m=read(); for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(); for(int i=1;i<=m;i++) q[i].id=i; sort(q+1,q+m+1,cmp); int p=1; for(int i=1;i<=n;i++){ if(pla[a[i]]){ add(pla[a[i]],-1); } add(i,1);pla[a[i]]=i; while(q[p].r==i){ ans[q[p].id]=ask(i)-ask(q[p].l-1); p++; } } for(int i=1;i<=m;i++) printf("%d ",ans[i]); return 0; }