- 题意: 求l,r区间中不同数字的个数
- 思路: 主席树,第i颗树代表了插入a[i]后,1~i区间内的信息
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e4+10;
struct node{
int l,r,sum;
}sgt[N*40];
int a[N],root[N],now;
map<int,int> mp; // 记录上一次 k 出现的位置
void insert(int l,int r,int pre,int rt,int pos,int val){
sgt[rt].sum = sgt[pre].sum; // 先将前一颗树的信息赋给当前树
sgt[rt].l = sgt[pre].l; sgt[rt].r = sgt[pre].r;
if(l==r){ // 叶子节点直接更新 叶子节点sum 取值{0,1}
sgt[rt].sum += val; return ;
}
int mid = (l+r)>>1;
if(pos <= mid) insert(l,mid,sgt[pre].l,sgt[rt].l=++now,pos,val); // 去左子树插入
else insert(mid+1,r,sgt[pre].r,sgt[rt].r=++now,pos,val); // 去右子树插入
sgt[rt].sum = sgt[sgt[rt].l].sum + sgt[sgt[rt].r].sum; // 更新父亲
}
int query(int rt,int l,int r,int L,int R){
if(L >= l && R <= r) return sgt[rt].sum; // 区间包含
int mid = (L+R)>>1;
int res = 0;
if(l<=mid) res += query(sgt[rt].l,l,r,L,mid); // 询问左子树
if(r>mid) res += query(sgt[rt].r,l,r,mid+1,R); // 询问右子树
return res;
}
int main(){
int l,r,k,n,q;
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i){
// 如果没有出现过 直接在i处增加1个
if(!mp[a[i]]) insert(1,n,root[i-1],root[i]=++now,i,1);
else{ // 否则先在i插入 再在mp[a[i]]处反向插入,因为前面的个数不应该增加
insert(1,n,root[i-1],root[i]=++now,i,1);
int cid = ++now; // 记录下新的树根
insert(1,n,root[i],cid,mp[a[i]],-1);
root[i] = cid; // 原来的树根不要了 要更新好的树根
}
mp[a[i]] = i; // 更新a[i]的位置
}
scanf("%d",&q);
for(int i=1;i<=q;++i){
scanf("%d%d",&l,&r);
printf("%d\n",query(root[r],l,r,1,n)); // 使用r的树根来查询
}
return 0;
}
有点想不通的是为什么最后的询问要用右端点的树来查询,而不能直接用最后一颗树查询(按理说后面的树不会更改前面树的信息吧...)