[SDOI2009]HH的项链
题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入格式
第一行:一个整数N,表示项链的长度。
第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。
第三行:一个整数M,表示HH 询问的个数。
接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
输出格式
M 行,每行一个整数,依次表示询问对应的答案。
输入 #1
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出 #1
2
2
4
说明/提示
对于所有数据,n,m<=10^6
这道题好像只能用树状数组,话说不是所有树状数组能做的,线段树都能做吗? 但是这道题线段树就tm超时,快读勉强卡过;真不知道为啥;
思路:维护一个权值线段树,先把要求的区间按右边界由小到大排序,然后遍历整个项链,如果一个贝壳在前面已经出现过,把这个贝壳前面出现的位置的权值改为0,这个新位置变为1;遍历一遍就行;
核心代码:
int now=1;
for(int i=1;i<=n;i++){
if(last[c[i]])//前面已经出现过
change(1,1,n,last[c[i]],0);//单点修改为0
last[c[i]]=i;
change(1,1,n,i,1);//单点修改为1
while(p[now].r==i){//把右边界为i的都算出来
p[now].an=ask(1,1,n,p[now].l,p[now].r);//区间查询
now++;
}
}
update:
我现在知道为啥了,线段树的常数太大,以后像这样循环加线段树的题目,数据又特别大的,就用树状数组做了;
代码:
#include<bits/stdc++.h>
using namespace std;
int a[1000010];
struct Node{
int L,R,post,date;
}dian[1000010];
bool cmp(Node p,Node q){
return p.R<q.R;
}
bool cm(Node p,Node q){
return p.post<q.post;
}
int vis[1000010];
int n,m;
int b[1000010],c[1000010];
int lowbit(int x){
return x&(-x);
}
void updata(int i,int k){//在i位置加上k
while(i <= n){
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i){//求A[1 - i]的和
int res = 0;
while(i > 0){
res += c[i];
i -= lowbit(i);
}
return res;
}
int main(){
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",&dian[i].L,&dian[i].R);
dian[i].post=i;
}
sort(dian+1,dian+1+m,cmp);
int now=1;
for(int i=1;i<=n;i++){
if(vis[a[i]]){
updata(vis[a[i]],-1);
}
vis[a[i]]=i;
updata(i,1);
while(dian[now].R==i){
dian[now].date=getsum(dian[now].R)-getsum(dian[now].L-1);
now++;
}
}
sort(dian+1,dian+1+m,cm);
for(int i=1;i<=m;i++) printf("%d\n",dian[i].date);
return 0;
}