zoukankan      html  css  js  c++  java
  • SPOJ DQUERY D-query 离线+树状数组

    本来是想找个主席树的题目来练一下的,这个题目虽说可以用主席树做,但是用这个方法感觉更加叼炸天

    第一次做这种离线方法,所谓离线,就在把所有询问先存贮起来,预处理之后再一个一个操作

    像这个题目,每个操作要求区间不同元素的个数,我盲目去查的话,某个元素在之前如果出现了,我把他算在当前区间也不好,算在之前的区间也不好,都会出错。

    一个好的方法就是把区间排好序,针对某个区间在树状数组上更新以及查询相应值,这样能准确查出结果,但又不影响之后的查询

    具体来说,先把区间按右端点进行排序(我一开始按左端点排,想错了),然后从小区间开始,然后树状数组的含义就是指以当前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了)

    总之:

    所以这就是这个离线用法的妙处,尤其要理解树状数组在这个题目代表的含义是什么,即当前 以r结尾的区间的元素种类个数,为了维护这个值的准确性,必须把没出现过的,加入到树状数组中,之前已经出现过了并且再次出现的,以再次出现的位置为准。 

    每次对区间不用一开始就扫,每个就只要扫一次就可以了,用个map记录哪个出现了,出现在什么位置,就不用重新扫了,否则会超时,这样,总共也就扫了n下而不是n*q

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <map>
    using namespace std;
    const int N = 300010;
    int n;
    map<int,int>mp;
    struct BIT
    {
        int c[N];
        void init(int n)
        {
            for (int i=0;i<=n;i++) c[i]=0;
        }
        void add(int loc,int v)
        {
            while (loc<=n)
            {
                c[loc]+=v;
                loc+=loc&(-loc);
            }
        }
        int sum(int loc)
        {
            int ret=0;
            while (loc){
                ret+=c[loc];
                loc-=loc&(-loc);
            }
            return ret;
        }
    }T;
    struct node
    {
        int l,r,id;
        bool operator <(const node&rhs) const{
            //if (l==rhs.l) return r<rhs.r;
            return r<rhs.r;
        }
    }query[1000000+10];
    int ans[1000000+10];
    int A[N];
    int main()
    {
        while (scanf("%d",&n)!=EOF)
        {
            mp.clear();
            T.init(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",&query[i].l,&query[i].r);
                query[i].id=i;
            }
            sort(query,query+q);
            int cur=1;
            for (int i=0;i<q;i++){
                for (int j=cur;j<=query[i].r;j++){
                    if (mp.find(A[j])!=mp.end()){
                        T.add(mp[A[j]],-1);
                    }
                    T.add(j,1);
                    mp[A[j]]=j;
                }
                cur=query[i].r+1;
               ans[query[i].id]=T.sum(query[i].r)-T.sum(query[i].l-1);
            }
            for (int i=0;i<q;i++){
                printf("%d
    ",ans[i]);
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    luogu P1833 樱花 看成混合背包
    luogu P1077 摆花 基础记数dp
    luogu P1095 守望者的逃离 经典dp
    Even Subset Sum Problem CodeForces
    Maximum White Subtree CodeForces
    Sleeping Schedule CodeForces
    Bombs CodeForces
    病毒侵袭持续中 HDU
    病毒侵袭 HDU
    Educational Codeforces Round 35 (Rated for Div. 2)
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3879517.html
Copyright © 2011-2022 走看看