题目:点击打开链接
题意:
给出n个数,给出q个询问,问你区间内不同数的个数是多少。
主席树:
主席树就是用一颗模拟的线段树控制往左右走,加的是主席树上存的东西。
pre表示前一个数出现的位置,为了找到pre还要用last记录某数最后出现的位置。
跟求第k大很像,求第k大存的是区间内的数量,对应线段树的叶子结点是离散化后的a[i],而这里变成了pre[i],l到r的不同数的数量只需要求区间中pre[i]<l的数量。
查询时第k大找到那个位置返回那个数就行,这里返回sum(区间内的数量)的累加值。
一般数据是从1开始,这里某个数是第一次出现的话,pre[i]=0,所以要从0开始建树。
#include<bits/stdc++.h>
#define ll long long int
using namespace std;
const int maxn=2e5+10;
const int maxm=1e6+10;
ll read(){
ll x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n, q;
int cnt;
struct node{
int l,r;
int sum;
}t[maxn * 20];
int root[maxn];
int last[maxm],pre[maxn];
void update(int pos,int &rt,int l,int r){
t[++cnt]=t[rt];
rt=cnt;
t[rt].sum++;
if(l==r) return ;
int mid=(l+r)>>1;
if(pos<=mid) update(pos,t[rt].l,l,mid);
else update(pos,t[rt].r,mid+1,r);
}
int query(int x,int y,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr) return t[y].sum-t[x].sum;
else{
int mid=(l+r)>>1,ans=0;
if(ql<=mid) ans+=query(t[x].l,t[y].l,l,mid,ql,qr);
if(mid<qr) ans+=query(t[x].r,t[y].r,mid+1,r,ql,qr);
return ans;
}
}
int main(){
n=read();
for(int i=1;i<=n;i++){
int x=read();
pre[i]=last[x];
last[x]=i;
}
for(int i=1;i<=n;i++){
root[i]=root[i-1];
update(pre[i],root[i],0,n);
}
q=read();
while(q--){
int ql=read(),qr=read();
printf("%d\n",query(root[ql-1],root[qr],0,n,0,ql-1));
}
return 0;
}