D-query(主席樹-區間不同數的個數)
阿新 • • 發佈:2018-12-24
大體題意:
給你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 3hide comments