1. 程式人生 > >SPOJ3267 D-query(可持久化線段樹)

SPOJ3267 D-query(可持久化線段樹)

題意: 輸入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; }