P1972 [SDOI2009] HH的项链 题解
:
给定长度为 的 序列,求 段区间不同数的个数。()
观察数据范围, 的范围不是 ,有极大可能以此来解题。
事实上,如果使用暴力,即使用前缀和。思路如下:
对于 :
-
未重复,直接加入。
-
重复,如果直接加入,会导致 被统计数次。可将 赋值为 。前缀和此时会失效,需重新统计。因为只有 ,可定义 数组。:值为 的 数组元素第一次出现的下标。
时间复杂度为 。
超时了。但观察以上结论,易得需要单点修改,区间查询。
树状数组可用 完成上述操作。
再对答案离线处理,将区间对 升序排序。在处理 时,就可以继承前面。复杂度为 。最后将离线处理的数据按原顺序输出。
如使用结构体,代码非常冗长。
此时可以定义 ,此时 即为 ,对 排序,但比较 (等价 ),仍有对 排序的效果。虽然 为原元素,但改变 ,通过映射排序,此时 为以 为关键字排序的 ,可以实现代码的简洁化。
#include<bits/stdc++.h>
using namespace std;
#define _x(i) x[mp[i]]
#define _y(i) y[mp[i]]
const int N=1e6+1;
int t[N],o,h,x[N],y[N],vis[N],s[N],mp[N],j=1,as[N],be[N];
int n,m,a[N];
int lowbit(int x) {
return x & -x;
}
void Plus(int i,int q) {
while(i<=n) {
t[i]+=q;
i+=lowbit(i);
}
}
int ans(int r) {
int sr=0;
while(r>=1) {
sr+=t[r];
r-=lowbit(r);
}
return sr;
}
bool cmp(int p,int q) {
return y[p]<y[q];
}
int main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
cin>>m;
for(int i=1;i<=m;i++) {
scanf("%d%d",&x[i],&y[i]);
mp[i]=i;
}
sort(mp+1,mp+m+1,cmp);
for(int i=1;i<=m;i++) {
while(j<=_y(i)) {
if(vis[a[j]]) {
Plus(vis[a[j]],-1);
}
vis[a[j]]=j;
Plus(j,1);
j++;
}
as[mp[i]]=ans(_y(i))-ans(_x(i)-1);
}
for(int i=1;i<=m;i++) {
cout<<as[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号