题目描述
HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。
有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。
输入格式
一行一个正整数 nn,表示项链长度。
第二行 nn 个正整数 a_iai,表示项链中第 ii 个贝壳的种类。
第三行一个整数 mm,表示 HH 询问的个数。
接下来 mm 行,每行两个整数 l,rl,r,表示询问的区间。
输出格式
输出 mm 行,每行一个整数,依次表示询问对应的答案。
输入输出样例
输入 #1复制
6 1 2 3 4 3 5 3 1 2 3 5 2 6
输出 #1复制
2 2 4
思路:用一个可持久化线段树来维护第i个位置上的数的贡献
每个数的贡献算在最右一次出现的位置
如果这个数在前面没有出现过那么我们就在这个位置+1
如果在前面出现过的话,我们就把在它上一个出现的位置-1,在这个位置+1
这样就保证了一个数如果前面出现过,那么前面的贡献会被减掉,贡献只算在最后一次出现的位置上
然后就求这段区间里的贡献总和就可以了
(测评机不太稳定这个代码有时候ac有时候会t一个测试点,多交几次就好了)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
struct name{
int l,r,sum;
}tr[N*50];
int n,m;
int idx;
int a[N];
int root[N];
int pre[N];
int build(int l,int r){
int p=++idx;
if(l==r)return p;
int mid=l+r>>1;
tr[p].l =build(l,mid);
tr[p].r =build(mid+1,r);
return p;
}
int modify(int p,int l,int r,int x,int v){
int q=++idx;
tr[q]=tr[p];
if(l==r){
tr[q].sum +=v;
return q;
}
int mid=l+r>>1;
if(x<=mid) tr[q].l =modify(tr[p].l ,l,mid,x,v);
else tr[q].r =modify(tr[p].r ,mid+1,r,x,v);
tr[q].sum =tr[tr[q].l ].sum +tr[tr[q].r ].sum ;
return q;
}
int query(int p,int l,int r,int k){
if(l==r)return tr[p].sum ;
int mid=l+r>>1;
if(k<=mid) return query(tr[p].l ,l,mid,k)+tr[tr[p].r ].sum ;
else return query(tr[p].r ,mid+1,r,k);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
root[0]=build(1,n);
for(int i=1;i<=n;i++){
if(pre[a[i]]==0){
root[i]=modify(root[i-1],1,n,i,1);
}else{
int t=modify(root[i-1],1,n,pre[a[i]],-1);
root[i]=modify(t,1,n,i,1);
}
pre[a[i]]=i;
}
scanf("%d",&m);
while(m--){
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(root[r],1,n,l));
}
return 0;
}