版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82382981
题意:给你n个按顺序排列的数a1,a2,...(1 ≤ n ≤ 30000,1 ≤ ai ≤ 1e6),以及q次询问(1 ≤ q ≤ 200000),每个询问都有两个值l,r,让你求出每个询问中[l,r]的区间内不同元素的个数。
解法:很容易看出,假如你已经求出了区间[l,r]内的答案,你就能在O(1)的时间内求出区间[l-1,r],[l+1,r],[l,r-1]或者[l,r+1]内的答案,因此可以考虑用莫队算法。
莫队算法是什么呢?简而言之,就是通过不断移动区间的左右端点l,r值来完成一系列询问的求解。为了使l和r的移动次数尽可能小,我们需要通过对询问进行合理的排序,然后依次求解。怎样排序呢?我们可以把n个数分为sqrt(n)个小区间,每个小区间的元素个数也为sqrt(n)(除了个别区间),然后把区间按照左端点值l所在分块的不同进行排序,对在同一分块内的区间,将它们按照右端点值r的大小进行排序。换言之,就是把区间的左端点值l的所在分块作为第一关键字,右端点值r作为第二关键字进行排序,这样可以使得端点移动得尽可能少,复杂度约为O(sqrt(n)*q),证明也不是很困难,可以自己尝试一下。
顺便推荐两篇我认为比较适合入门的文章:
https://www.cnblogs.com/hzf-sbit/p/4056874.html
https://blog.csdn.net/nhl19961226/article/details/81412134
#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+100;
const int M=1e6+100;
const int maxq=2e5+100;
int a[N],in[N],sz,cnt[M],n,Q;
struct Query
{
int l,r,i,ans;
bool operator<(const Query& b)const
{
return in[l]!=in[b.l]?in[l]<in[b.l]:r<b.r;
}
};
Query qr[maxq];
int ans[maxq];
void solve()
{
sort(qr,qr+Q);
memset(cnt,0,sizeof cnt);
memset(ans,0,sizeof ans);
int L=0,R=-1,now=0;
for(int i=0; i<Q; ++i)
{
int l=qr[i].l,r=qr[i].r;
while(L<l)
{
cnt[a[L++]]--;
if(cnt[a[L-1]]==0)--now;
}
while(L>l)
{
cnt[a[--L]]++;
if(cnt[a[L]]==1)++now;
}
while(R<r)
{
cnt[a[++R]]++;
if(cnt[a[R]]==1)++now;
}
while(R>r)
{
cnt[a[R--]]--;
if(cnt[a[R+1]]==0)--now;
}
ans[qr[i].i]=now;
}
}
int main()
{
scanf("%d",&n);
sz=sqrt(n);
for(int i=1; i<=n; ++i)
{
scanf("%d",&a[i]);
in[i]=(i-1)/sz;
}
scanf("%d",&Q);
for(int i=0; i<Q; ++i)
{
scanf("%d%d",&qr[i].l,&qr[i].r);
qr[i].i=i;
}
solve();
for(int i=0; i<Q; ++i)
printf("%d\n",ans[i]);
return 0;
}