这题有离线和在线两种做法。
离线:将查询区间按左端点排序,预处理出所有数下一次的出现位置,一开始将所有第一次出现的数a[i]++,之后当扫到这个数的时候a[next[i]]++,相当于差分,给之后的位置答案+1,因为查询区间左端点排序了,所以再也查不到当前点,这个数对答案有贡献的区间只有右端点在这个数下一次出现的位置右边的区间,当扫到查询区间左端点时当前答案为sum(r)-sum(l-1)。
#include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> #include<cmath> #include<map> #define ll long long using namespace std; const int maxn=500010,inf=1e9; struct poi{int l,r,pos;}q[maxn]; int n,m,x,y,z,tot,N; int tree[maxn],a[maxn],b[maxn],ans[maxn],next[maxn],pre[maxn]; bool v[maxn]; inline void read(int &k) { int f=1;k=0;char c=getchar(); while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar(); k*=f; } inline bool cmp(poi a,poi b){return a.l<b.l;} inline int lowbit(int x){return x&-x;}; inline void add(int x){for(;x<=n;x+=lowbit(x))tree[x]++;} inline int query(int x){int sum=0;for(;x;x-=lowbit(x))sum+=tree[x];return sum;} int main() { read(n); for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];N=n; sort(b+1,b+1+N);N=unique(b+1,b+1+N)-b-1; for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+N,a[i])-b; for(int i=n;i;i--)next[i]=pre[a[i]],pre[a[i]]=i; for(int i=1;i<=n;i++) if(!v[a[i]])add(i),v[a[i]]=1; read(m); for(int i=1;i<=m;i++) { read(q[i].l);read(q[i].r); q[i].l--;q[i].pos=i; } sort(q+1,q+1+m,cmp); for(int i=0,j=1;i<=n&&j<=m;i++) { if(next[i])add(next[i]); while(j<=m) { if(q[j].l!=i)break; ans[q[j].pos]=query(q[j].r)-query(q[j].l); j++; } } for(int i=1;i<=m;i++)printf("%d\n",ans[i]); return 0; }