题目大意
对于数列
a[1⋯n]
a
[
1
⋯
n
]
,静态查询
[i,j]
[
i
,
j
]
中不同数的个数。
数据范围
1⩽n⩽500000,1⩽m⩽200000,0⩽ ai ⩽1000000
1
⩽
n
⩽
500000
,
1
⩽
m
⩽
200000
,
0
⩽
a
i
⩽
1000000
题解
定义
fi,j
f
i
,
j
表示考虑前
i
i
个方格, 位置为是否为最后一个出现的
aj
a
j
。
则对于区间
[a,b]
[
a
,
b
]
,则显然地,答案就是
∑i=abfb,i
∑
i
=
a
b
f
b
,
i
因为是区间求和,考虑前缀和降低复杂度。可是计算
fi,j
f
i
,
j
的时间复杂度为
O(n2)
O
(
n
2
)
,已经超时了。
注意到,可以用
fi
f
i
序列直接算出
fi+1
f
i
+
1
序列。
具体方法是记录下上一个
aj
a
j
的位置
lastaj
l
a
s
t
a
j
,则可以直接得到
fi+1,lastaj=0,fi+1,aj=1
f
i
+
1
,
l
a
s
t
a
j
=
0
,
f
i
+
1
,
a
j
=
1
,其他元素满足
fi+1=fi
f
i
+
1
=
f
i
。
其实也可以直接覆盖
fi,j
f
i
,
j
,把
i
i
那一维降掉。
但这样计算前缀和就不太靠谱了,可以换成树状数组或者线段树,考虑到是改点求段,因此选用树状数组。
因此可以 离线计算。区间按照右端点排序。每次计算向右拓展。然后依次回答右端点相同的询问。时间复杂度为 。
PS:考虑到
ai>n
a
i
>
n
,因此可以对数据进行离散化。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;
const int maxm=200010;
const int maxi=1000010;
#define lowbit(x) ((x)&(-(x)))
int c[maxn],n;//树状数组相关
inline void add(int x,int d){
for(;x<=n;x+=lowbit(x))
c[x]+=d;
}
inline int sum(int x){
int ans=0;
for(;x>0;x-=lowbit(x))
ans+=c[x];
return ans;
}
inline int sum(int x,int y){
return sum(y)-sum(x-1);
}
struct qual{//区间相关
int l,r,i;
inline bool operator<(const qual &x)const{//排序相关
return r<x.r;
}
}e[maxm];
int a[maxn],m;
int last[maxi];//上一个a[i]的位置
int ans[maxn];//对答案排序(离线)
int main(void)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&e[i].l,&e[i].r);
e[i].i=i;//离线相关
}
sort(e+1,e+1+m);
int lasr=0;
memset(c,0,sizeof c);
for(int i=1;i<=m;i++){//枚举区间
while(lasr<e[i].r){//扩展右端点
++lasr;
if(last[a[lasr]])//修改为0
add(last[a[lasr]],-1);
add(lasr,1);//修改为1
last[a[lasr]]=lasr;//标记
}
ans[e[i].i]=sum(e[i].l,e[i].r);//记录答案
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}