比如(1 3 5 2 1),对于所有(r=5)的询问,因为第(5)个数是(1),所以前面所有的(1)都不用关心,靠右边的数一定是更“优”的
所以我们每次只需要靠右的数看他能不能更新前面的数即可。
考虑使用梳妆数组维护
具体实现是按照询问的(r)排序,不停往右扫描,记录一个数上一次出现的位置,每次都在当前位置插入(1),如果出现过就在上一个出现的位置插入(-1)以消除上面的数的影响,扫描到一个(r)就进行区间求和,最后按编号输出答案就行
具体实现如下,一个简单的区间查询+单点修改
(Code)
#include<cstdio>
#include<iostream>
#include<algorithm>
#define maxn 1000010
#define re register
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
};
struct P{
int l,r,id;
}node[maxn];
int tre[maxn<<2];
int tmp,n,m,ans[maxn],a[maxn],vis[maxn];
bool cmp(P A,P B)
{
return A.r<B.r;
}
void add(int x,int k)
{
while(x<=n)
{
tre[x]+=k;
x+=(x&-x);
}
}
int query(int x)
{
int tmp2=0;
while(x>=1)
{
tmp2+=tre[x];
x-=(x&-x);
}
return tmp2;
}
int Query(int l,int r)//树状数组单点修改+区间查询
{
return query(r)-query(l-1);
}
int main()
{
n=read();
for(re int i=1;i<=n;++i) a[i]=read();
m=read();
for(re int i=1;i<=m;++i)
{
node[i].l=read(),node[i].r=read(),node[i].id=i;
}
sort(node+1,node+m+1,cmp);
tmp=1;
for(re int i=1;i<=m;++i)
{
for(re int j=tmp;j<=node[i].r;++j)//按r排序的原因上面说过
{
if(vis[a[j]])
add(vis[a[j]],-1);
add(j,1);//不管什么时候这里都要加
vis[a[j]]=j;
}
tmp=node[i].r+1;//记录下一次开始的位置,保证扫描是$O(n)$的
ans[node[i].id]=Query(node[i].l,node[i].r);//记录对应编号的答案
}
for(re int i=1;i<=m;++i)
printf("%d
",ans[i]);//按编号输出
}