luogu SP3267 DQUERY - D-query(主席树求区间内不同数的个数)

传送:https://www.luogu.org/jump/spoj/3267

题意:

给定一个长度为$n$的序列,$q$个询问,问区间$[l,r]$内的不同数的个数。

数据范围:

$1<=n<=30000,1<=a_i<=10^6,1<=q<=200000$。

分析:

求区间内不同数的个数。很明显是莫队的裸题。

那么对于主席树怎么做呢?

统计不同数的个数,那么就是看一个数是否在同一区间内出现多次。

那么就可以记录一个数字后一个出现的位置,即第$i$个位置的数字后一个出现的位置为$nxt[i]$。

统计答案的时候就是可以求区间$[l,r]$内的$nxt[i]$是否有$<=r$的,这样就代表有重复。

换一句话就是求区间$[l,r]$内,$nxt[i]$在$[r+1,n+1]$范围内的个数。

那么主席树维护数组下标,每个主席树是权值线段树,代表后一个位置出现数字的个数。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=3e4+10;
 4 struct node{
 5     int l,r,num;
 6 }tree[maxn*100];
 7 int size=0,root[maxn],a[maxn],nxt[maxn];
 8 map<int,int> mp;
 9 void update(int &x,int y,int l,int r,int dex){
10     x=++size; tree[x]=tree[y]; tree[x].num++;
11     if(l==r) return ;
12     int mid=(l+r)>>1;
13     if (dex<=mid) update(tree[x].l,tree[y].l,l,mid,dex);
14     else update(tree[x].r,tree[y].r,mid+1,r,dex); 
15 }
16 int query(int x,int y,int l,int r,int xx,int yy){
17     if (xx<=l && r<=yy) return tree[y].num-tree[x].num;
18     int mid=(l+r)>>1;
19     int res=0;
20     if (xx<=mid) res+=query(tree[x].l,tree[y].l,l,mid,xx,yy);
21     if (yy>mid) res+=query(tree[x].r,tree[y].r,mid+1,r,xx,yy);
22     return res;
23 }
24 int main(){
25     int n;scanf("%d",&n);
26     for (int i=1;i<=n;i++) scanf("%d",&a[i]);
27     mp.clear();
28     for (int i=n;i>=1;i--){
29         if (mp[a[i]]==0) nxt[i]=n+1;
30         else nxt[i]=mp[a[i]];
31         mp[a[i]]=i;
32     } 
33     for (int i=1;i<=n;i++){
34         update(root[i],root[i-1],1,n+1,nxt[i]);
35     }
36     int m,x,y;scanf("%d",&m);
37     while(m--){
38         scanf("%d%d",&x,&y);
39         int ans=query(root[x-1],root[y],1,n+1,y+1,n+1);
40         printf("%d\n",ans);
41     }
42     return 0;
43 }

 

posted @ 2019-09-03 17:40  Changer-qyz  阅读(185)  评论(0编辑  收藏  举报