- SPOJ 3267 DQUERY - D-query -
题意:
給定一个长度为 n 的数组,对于之后的 m 次询问,求询问区间内不同元素的数目。
数据范围:
1 ≤ n ≤ 30000,1 ≤ ai ≤ 106,1 ≤ m ≤ 200000
解题思路:
树状数组
对询问按右区间从小到大排序
,扫描一遍数组,记录每个元素最后一次出现的位置
,每遇到一个元素,若它在前面出现过,则在原来的位置-1,在现在的位置+1;若未在前面出现过,则直接在现在的位置+1,每次处理到右区间时,就计算一次该询问的结果并保存在数组里,最后按顺序输出即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f
#define zero 1e-7
typedef long long ll;
const int N=1e6+5;
int n, m, a[N], mark[N], sum[N]={0}, ans[N]={0};
struct node {
int l, r, id;
}q[N];
bool cmp(node x, node y) {
return x.r<y.r;
}
int lowbit(int x) {
return x&(-x);
}
void add(int x, int c) {
while(x<=n) {
sum[x]+=c;
x+=lowbit(x);
}
return ;
}
int query(int x) {
int res=0;
while(x) {
res+=sum[x];
x-=lowbit(x);
}
return res;
}
int main() {
scanf("%d", &n);
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);
memset(mark, -1, sizeof(mark));
int l=1;
for(int i=1; i<=m; i++) {
for(int j=l; j<=q[i].r; j++) {
if(mark[a[j]]!=-1) {
add(mark[a[j]], -1);
}
add(j, 1);
mark[a[j]]=j;//记录a[j]最后一次出现的位置
}
l=q[i].r+1;
ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
}
for(int i=1; i<=m; i++)
printf("%d\n", ans[i]);
return 0;
}