查询区间有多少种 数
查询次数 和 数列长度 都很大
我们如果要用前缀和那种方法来算的话 就是 i j 等于 1 j 减去 1 i-1
这样直接查询肯定是不行的, 例如 1 2 2 1 3 1位置和4位置都有1 1 到 3 有 2 种数 1 到 5 有 3 种数 ,那相减就是 4 到 5 有1种数 答案显然是2种数 因为 前面的区间和后面的区间有相同的数 ,这样相减就会忽略后面的数
所以我们要对 m 次查询进行离线化 ,按右边界r从小到大进行排序 然后数列中相同的数只保留最后一次出现的位置 ,之前出现过的位置的c值就要-1
然后我们每一个位置的数只需要遍历一次 而不需要遍历 n*Q次
树状数组统计 区间和的值 !!!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<cstring>
using namespace std;
const int N = 2e5 + 10;
int a[N],ans[N];
map<int , int> ma;
struct node
{
int l,r,id;
bool operator< (node &a) const {
return r < a.r;
}
}qe[N];
int c[N];
int lowbit(int x)
{
return x&(-x);
}
void update(int pos,int val)
{
while(pos<=N){
c[pos] += val;
pos += lowbit(pos);
}
}
int sum(int x)
{
int sum = 0;
while(x > 0){
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int q;
scanf("%d",&q);
for(int i=0;i<q;i++){
scanf("%d%d",&qe[i].l,&qe[i].r);
qe[i].id = i;
}
sort(qe,qe+q);
memset(c,0,sizeof(c));
ma.clear();
int cur = 1;
for(int i=0;i<q;i++)
{
for(int j=cur;j<=qe[i].r;j++)
{
if(ma.find(a[j])!=ma.end()){
update(ma[a[j]],-1);
}
update(j,1);
ma[a[j]] = j;
}
cur = qe[i].r + 1;
ans[qe[i].id] = sum(qe[i].r) - sum(qe[i].l-1);
}
for(int i=0;i<q;i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}
离线 + 树状数组 来查询区间有多少种数