1. 程式人生 > >D-query(主席樹-區間不同數的個數)

D-query(主席樹-區間不同數的個數)

大體題意:

給你n 個數,給你q個詢問,每個詢問問你某個區間上不同數的個數是多少?

思路:

主席樹入門題:

簡單記錄一下:

這裡先建立一個完整的線段樹,這裡的區間就代表區間了,不再是第幾大了,

定義的sum 是這個區間上的不同數的個數有幾個。

因為是主席樹嘛,所以肯定要建立n 棵線段樹,每個線段樹是以每個位置的數為根,比如說該建立第i 個線段樹了,如果這個數字之前沒有出現過,那麼我們直接以位置為劃分依據,線上段樹上包含這個位置的加1即可,表示這個區間上又多了一種數。

但是如果這個數字出現過了,我們先求出上一個同樣數在哪裡出現,我們就在第i 個線段樹上以那個位置為劃分依據給它減去1,在在第i 個線段樹上 包含位置的i 的區間加1,這樣我們就保證了 區間中數字不重複,只保留最後一個。

update程式碼:

int update(int pos,int c,int v,int l,int r){
    int nc = ++cnt;
    p[nc] = p[c];
    p[nc].sum += v;
    if (l == r) return nc;
    int m = l+r>>1;
    if (m >= pos){
        p[nc].l = update(pos,p[c].l,v,l,m);
    }
    else {
        p[nc].r = update(pos,p[c].r,v,m+1,r);
    }
    return nc;
}

在來說查詢:

比如說我們要查詢[L,R]這個區間上不同數的個數,我們就以L 為劃分依據,在第R個線段樹上進行查詢,當發現往左走時,右邊是一個完整的,我們直接加上右兒子的sum即可,在遞迴左兒子,如果發現是往右走,那麼直接遞迴右邊就好了,左邊的不用加,因為左邊的比L小,肯定會加多。

這樣加到底,我們就可以得到一個完整區間[L,R]上不同數的個數。

int query(int pos,int c,int l,int r){
    if (l == r) return p[c].sum;
    int m = l + r >> 1;
    if (m >= pos){
        return p[p[c].r ].sum + query(pos,p[c].l,l,m);

    }
    else return query(pos,p[c].r,m+1,r);
}


完整程式碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 30000 + 10;



int n,q;
int cnt = 0;
struct Node{
    int l,r,sum;
}p[maxn*40];

int la[1000000 + 10];

int a[maxn];
int root[maxn];

int build(int l,int r){
    int nc = ++cnt;
    p[nc].sum = 0;
    p[nc].l = p[nc].r = 0;
    if (l == r) return nc;
    int m = l + r >> 1;
    p[nc].l = build(l,m);
    p[nc].r = build(m+1,r);
    return nc;
}

int update(int pos,int c,int v,int l,int r){
    int nc = ++cnt;
    p[nc] = p[c];
    p[nc].sum += v;
    if (l == r) return nc;
    int m = l+r>>1;
    if (m >= pos){
        p[nc].l = update(pos,p[c].l,v,l,m);
    }
    else {
        p[nc].r = update(pos,p[c].r,v,m+1,r);
    }
    return nc;
}

int query(int pos,int c,int l,int r){
    if (l == r) return p[c].sum;
    int m = l + r >> 1;
    if (m >= pos){
        return p[p[c].r ].sum + query(pos,p[c].l,l,m);

    }
    else return query(pos,p[c].r,m+1,r);
}

int main(){
    scanf("%d",&n);
    memset(la,-1,sizeof la);
    for (int i = 1; i <= n; ++i){
        scanf("%d",a+i);
    }
    root[0] = build(1,n);

    for (int i = 1 ; i <= n; ++i){
        int v = a[i];
        if (la[v] == -1){
            root[i] = update(i,root[i-1],1,1,n);
        }
        else{
            int t = update(la[v],root[i-1],-1,1,n);
            root[i] = update(i,t,1,1,n);
        }
        la[v] = i;
    }

    scanf("%d",&q);
    while(q--){
        int x,y;
        scanf("%d %d",&x, &y);
        printf("%d\n",query(x,root[y],1,n));
    }
    return 0;
}


DQUERY - D-query


Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, ..., aj.

Input

  • Line 1: n (1 ≤ n ≤ 30000).
  • Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
  • Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
  • In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).

Output

  • For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, ..., aj in a single line.

Example

Input
5
1 1 2 1 3
3
1 5
2 4
3 5

Output
3
2
3 
hide comments