题意:询问你[l,r]范围内不同的数字有几个。
思路;这题查询是离线的,并且我们维护数组cnt,和结果ans(就是有几个不同的数字),cnt[i]=j,表示在当前的区间[L,R]中,第i个数字出现了j次。如果我们加上一个数字后cnt[i]是1,那么就说明这个数字是第一次出现,那就ans++,相反,如果我们删除一个数字i后,cnt[i]==0,那就说明该区间内不同的数字数量-1,即ans–;
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn = 3e4 + 5;
int block, n, q;
int a[maxn], belog[maxn], cnt[1000005], ans;
//a存储数据,belog[i]=j,表示第i个数字属于第j块,cnt存储一个数字出现了几次,ans是有几个不同的数字
struct node {
int l, r, id, val;//l r是区间的范围,id 便于最后输出,val是该组查询的答案
} e[200005];
bool cmp1(node a, node b) {
return belog[a.l] < belog[b.l] || (belog[a.l] == belog[b.l] && a.r < b.r);
}
bool cmp2(node a, node b) {
return a.id < b.id;
}
void updata(int x, int add) {
if(add == 1) {
cnt[a[x]]++;
if(cnt[a[x]] == 1) ans++;//如果加上该数字i后cnt[i]是1 ,就说明i是该区间第一次出现
} else if(add == -1) {//删除也同理
cnt[a[x]]--;
if(cnt[a[x]] == 0) ans--;
}
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
scanf("%d", &q);
for(int i = 1; i <= q; i++) {
scanf("%d%d", &e[i].l, &e[i].r);
e[i].id = i;
}
block = sqrt(n);
for(int i = 1; i <= n; i++) {//分块
belog[i] = (i - 1) / block + 1;
}
sort(e + 1, e + 1 + q, cmp1);//排序
cnt[a[1]]++;//初始化
ans = 1;
for(int i = 1, l = 1, r = 1; i <= q; i++) {//进行q次离线查询
for(; r < e[i].r; r++) {
updata(r + 1, 1);
}
for(; r > e[i].r; r--) {
updata(r, -1);
}
for(; l > e[i].l; l--) {
updata(l - 1, 1);
}
for(; l < e[i].l; l++) {
updata(l, -1);
}
e[i].val = ans;
}
sort(e + 1, e + 1 + q, cmp2);
for(int i = 1; i <= q; i++) {
printf("%d\n", e[i].val);
}
return 0;
}