SPOJ3267 D-query(可持久化線段樹)
阿新 • • 發佈:2018-11-19
題意: 輸入N個數字,查詢區間[L,R]中有多少個不同的數字
(第i個數字為a[i])
思路:要維護的是不同的數字的數量,所以每個數只記最後出現的那一次。對N個位置每個位置建一棵線段樹,線段樹維護的是插入
a[i]後樹上各區間數字的數量。如果a[i]這個數字在前面被插入過,就在新建的樹中把 包含a[i]的上一個位置的 所有區間 的
值-1,然後再照常插入a[i],因此要用一個數組幾下a[i]上次插入時的位置i。查詢時只要查R對應的版本的線段樹就行了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 30010 ;
int t[maxn*30],tot,rts[maxn],lson[maxn*30],rson[maxn*30];
void add(int pos,int val,int l,int r,int &rt,int lst)
{
rt = ++tot;
t[rt] = t[lst]+val,lson[rt] = lson[lst],rson[rt] = rson[lst];
if(l==r)
return ;
int m = l + r >> 1;
if(pos<=m)
add(pos,val,l,m ,lson[rt],lson[lst]);
else
add(pos,val,m+1,r,rson[rt],rson[lst]);
}
int query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
return t[rt];
int m = l + r >> 1,ans = 0;
if(L<=m)
ans += query(L,R,l,m,lson[rt]);
if(R>m)
ans += query(L,R,m +1,r,rson[rt]);
return ans;
}
int N,a[maxn],x,y,Q,past[1000005];
int main()
{
scanf("%d",&N);
for(int i=1;i<=N;++i)
scanf("%d",&a[i]);
for(int i=1;i<=N;++i)
{
if(past[a[i]])//插入過
{
add(past[a[i]],-1,1,N,rts[i],rts[i-1]);//先把上個位置-1
add(i,1,1,N,rts[i],rts[i]);//再把新的位置+1
}
else
add(i,1,1,N,rts[i],rts[i-1]);
past[a[i]] = i;
}
scanf("%d",&Q);
while(Q--)
{
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y,1,N,rts[y]));
}
return 0;
}