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
题意就是查询区间不同元素的个数,有两种解法。
解法1:离线+树状数组,先把询问离线,并且按照右端点排序,然后从小区间开始,然后树状数组的含义就是指以当前r为结尾的前缀区间的元素种类数,简单点说,就是我当前扫到l , r区间,把l - r区间还没在树状数组上更新的值,更新一遍,在之前已经存在了的值先删掉再更新一遍,确保我确定的元素都是往r靠的,这样才能保证求取区间正确。举个栗子吧:比如我 1 2 2 1 3,当我r移到3的时候,加入前面的1还没在树状数组里更新过(但其实之前已经有读过1了)那就把之前的1的影响删掉,重新在这个3左边这个下标为4的位置给树状数组 add 1.这样确保之后不管我是查询 4 5 或者 1 5,元素1都只算了一次,但都没遗落(想想如果元素1一直在下标1那里,我查询4 5,就不会有1了)
//SPOJ DQUER 170ms 34816kb
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300010;
const int maxq = 1000010;
map <int, int> mp; //记录第一次出现的位置
struct Q{
int l, r, id;
Q(){}
Q(int l, int r, int id) : l(l), r(r), id(id){}
}q[maxq];
bool cmp(Q a, Q b){
return a.r < b.r;
}
namespace BIT{
int sum[maxn], n, m;
inline void init(){memset(sum, 0, sizeof(sum));}
inline int lowbit(int x){return x & -x;}
inline void add(int i, int v){for(;i <= n; i+=lowbit(i)) sum[i] += v;}
inline int query(int i){int res = 0; for(; i; i-=lowbit(i)) res += sum[i]; return res;}
}
using namespace BIT;
int a[maxn], ans[maxq];
int main(){
while(scanf("%d", &n) != EOF){
mp.clear();
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
scanf("%d", &m);
for(int i = 1; i <= m; i++){
scanf("%d%d", &q[i].l, &q[i].r); q[i].id = i;
}
sort(q + 1, q + m + 1, cmp);
int cur = 1;
for(int i = 1; i <= m; i++){
int id = q[i].id;
for(int j = cur; j <= q[i].r; j++){
if(mp.find(a[j]) != mp.end()){
add(mp[a[j]], -1);
}
add(j, 1);
mp[a[j]] = j;
}
cur = q[i].r + 1;
ans[id] = query(q[i].r) - query(q[i].l - 1);
}
for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
}
return 0;
}
解法2:有了主席树我们为什么要离线呢?对不对。对于一个数,如果以前没出现过,就插入到主席树中,否则就删除以前那个,再插入主席树。注意,所有的更新和删除都是建立了新的节点来保持其历史状态的。对于T[i]我们存的是从1到i区间的不同的数出现了多少个。然后这棵树是根据T[i - 1]来建立的。
//200ms 34816kb
//SPOJ DQUERY
#include <bits/stdc++.h>
using namespace std;
const int maxn = 30010;
int n, q, tot, a[maxn], nxt[maxn], b[maxn];
int T[maxn*40], lson[maxn*40], rson[maxn*40], val[maxn*40];
int build(int l, int r){
int rt = tot++; val[rt] = 0;
int mid = (l + r) / 2;
if(l != r){
lson[rt] = build(l, mid);
rson[rt] = build(mid + 1, r);
}
return rt;
}
int update(int root, int pos, int v){
int newroot = tot++, tmp = newroot;
int l = 1, r = n;
val[newroot] = val[root] + v;
while(l < r){
int m = (l + r) / 2;
if(pos <= m){
lson[newroot] = tot++; rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root]; r = m;
}
else{
rson[newroot] = tot++; lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root]; l = m + 1;
}
val[newroot] = val[root] + v;
}
return tmp;
}
int query(int root, int pos){
int ret = 0;
int l = 1, r = n;
while(l < pos){
int m = (l + r) / 2;
if(pos <= m){
ret += val[rson[root]];
root = lson[root];
r = m;
}
else{
l = m + 1;
root = rson[root];
}
}
return ret + val[root];
}
int main()
{
while(scanf("%d", &n) != EOF)
{
tot = 0;
memset(nxt, -1, sizeof(nxt));
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
b[i-1] = a[i];
}
//lisanhua
sort(b, b+n);
int cnt = unique(b, b + n)- b;
T[0] = build(1, n);
for(int i = 1; i <= n; i++){
int id = lower_bound(b, b+cnt, a[i]) - b;
if(nxt[id] == -1){
T[i] = update(T[i - 1], i, 1);
}
else{
int t = update(T[i-1], nxt[id], -1);
T[i] = update(t, i, 1);
}
nxt[id] = i;
}
scanf("%d", &q);
while(q--){
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", query(T[r], l));
}
}
return 0;
}