SPOJ - DQUERY D-query 离线 + 树状数组

查询区间有多少种 数

查询次数 和 数列长度 都很大

我们如果要用前缀和那种方法来算的话 就是 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;
}

离线 + 树状数组 来查询区间有多少种数

猜你喜欢

转载自blog.csdn.net/Hrbust_Final/article/details/80396432