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;
    }
    

      

  • 相关阅读:
    ARPPING
    Oracle RAC 连接
    Win7 DCOM 配置中我的电脑出现红色箭头并且无属性显示的解决方法
    Ping命令
    Linux & Oracle 安装目录说明
    TCP 四次握手
    wireshark使用
    jcaptcha组件小小改造解决Invalid ID, could not validate une
    JustSniffer
    java 自己定义异常,记录日志简单说明!留着以后真接复制
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3879517.html
Copyright © 2011-2022 走看看