题目大意
给定一个序列以及M个询问(L,R),求[L,R]内不同的数的个数。
分析
莫队镇楼。
先将询问记录下来,按照左端点排序,再将询问分成块,每块内部按照右端点排序。用两个指针,开始,在按照排序后结果遍历每个询问的时候不断去更新两个指针,使它们指向当前询问的,在移动过程中记录下每个数字出现的次数,并不断更新答案,结束后将当前的答案记录到数组对应位置上。
具体看代码。
当然,此题离线+树状数组可过且速度快于莫队。
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
#include <queue>
using namespace std;
struct qt {
int l,r,num;
}q[200005];
int n,len,a[50005],m,ans[200005],mp[1001001];
int app[50500],pl,pr,temp,tot;
bool cmp(qt a,qt b) {
if ((a.l-1)/len!=(b.l-1)/len) return (a.l-1)/len<(b.l-1)/len;
return a.r<b.r;
}
inline void update(int x) {
app[x]++;
if (app[x]==1) temp++;//当有一个数字出现次数从0->1,就说明ans+=1
}
inline void downdate(int x) {
app[x]--;
if (app[x]==0) temp--;//当有一个数字出现次数变为了0,就说明ans-=1
}
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%d",&a[i]);
if (!mp[a[i]]) mp[a[i]]=++tot;//离散化
a[i]=mp[a[i]];
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&q[i].l,&q[i].r),q[i].num=i;
len=sqrt(n);
sort(q+1,q+m+1,cmp);
pl=1,pr=0;
for (int i=1;i<=m;i++) {
while (pr<q[i].r)
update(a[++pr]);
while (pl>q[i].l)
update(a[--pl]);
while (pr>q[i].r)
downdate(a[pr--]);
while (pl<q[i].l)
downdate(a[pl++]);
ans[q[i].num]=temp;
}
for (int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}