测试地址:D-query
题目大意:有N个数,Q个询问,每次询问某个区间[L,R]内有多少不同的数。
做法:这题我用主席树做过,题解请看这里。
而这道题用莫队算法也能做,思考信息的转移:存储一个数组f表示每个数出现的次数,那么每次转移要么插入一个数,要么删除一个数,这样我们判断就很容易:当插入一个数时,如果原来这个数没有出现,那么答案+1;当删除一个数时,如果这个数原来就只剩一个了,那么答案-1。然后直接套莫队算法离线处理询问即可。
想法:这道题目用主席树写的话思想比较复杂(当然也有可能是更简单的方法我没想到),而用莫队算法就可以很优美的解决,所以说莫队算法对于大多数离线区间询问问题还是有很大的作用的。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,a[30010],f[1000010]={0};
struct query
{
int l,r,id,ans;
int pos;
}q[200010];
bool cmp1(query a,query b)
{
return a.pos<b.pos||(a.pos==b.pos&&a.r<b.r);
}
bool cmp2(query a,query b)
{
return a.id<b.id;
}
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
int block=(int)sqrt((double)n+0.5);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
q[i].pos=(q[i].l-1)/block;
}
sort(q+1,q+m+1,cmp1);
}
void transfer(int p,int &ans,int add)
{
bool flag=0;
if (f[a[p]]==0) flag=1;
f[a[p]]+=add;
if (flag&&f[a[p]]>0) ans++;
else if (f[a[p]]==0) ans--;
}
void solve()
{
int l=1,r=0,ans=0;
for(int i=1;i<=m;i++)
{
if (r<q[i].r) while(r<q[i].r) r++,transfer(r,ans,1);
if (q[i].l<l) while(q[i].l<l) l--,transfer(l,ans,1);
if (r>q[i].r) while(r>q[i].r) transfer(r,ans,-1),r--;
if (q[i].l>l) while(q[i].l>l) transfer(l,ans,-1),l++;
q[i].ans=ans;
}
sort(q+1,q+m+1,cmp2);
for(int i=1;i<=m;i++)
printf("%d
",q[i].ans);
}
int main()
{
init();
solve();
return 0;
}