zoukankan      html  css  js  c++  java
  • 2019南昌网络赛-I. Yukino With Subinterval 线段树套树状数组,CDQ分治

    TMD。。。这题卡内存卡的真优秀。。。

    所以以后还是别用主席树的写法。。。不然怎么死的都不知道。。。

    树套树中,主席树方法开权值线段树。。。会造成空间的浪费。。。这道题内存卡的很紧。。。

    由于树套树已经不需要持久化了,直接动态开点就完事了。。。用主席树方法开过不去,要么超内存,要么越界。。。

    大概思路。。。这题要求的[L,R]区间内,满足x<=a[i]<=y的连续的段数,

    这题大概是个套路题,我们很容易想到,我们把连续的区间变成单点,把一段区间,类似1 1 1 3 5 变成 1 0 0 3 5 这样我们

    只需要维护区间内部,在某个范围内数字的个数,这样的求法有个很显然的弊端如果1 0 0 3 5 序列, 我们求的区间是 2到5

    这样求出来的答案是2,但是答案是3,究其原因,是我们b[l]端点出了问题,如果b[l]是0,我们求出来的就比答案少一个,那

    么如何求出答案呢???很简单,可以先求出a[l+1]-a[r]的个数,这个算出来的是肯定不准确的,在b[l+1]=0的时候,在其他的

    情况下正确的,如果b[l+1]=0 或者b[l+1]!=0,我们只要判断a[l]是否满足,如果满足条件就+1,这样就保证b[l+1]的情况,并且

    在b[l+1]!=0的情况下,也是正确的。

    考虑修改,单点更新,如果当前点是a[l]==a[l-1]那么a[l]将变成一个新的左端点。

    如果修改后,a[l]==a[l+1]那么a[l+1]将不再是一个左端点,需要舍去。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int maxx = 2e5+6;
    struct node{
       int l,r;
       int cnt;
    }tree[maxx*220];
    int cnt,n,cnt1,cnt2;
    int root[maxx],a[maxx],trl[maxx],trr[maxx];
    void inserts(int &now,int l,int r,int pos,int w){
       if(!now)now=++cnt;
       tree[now].cnt+=w;
       if (l==r){
        return ;
       }
       int mid=(l+r)>>1;
       if (pos<=mid){
        inserts(tree[now].l,l,mid,pos,w);
       }else{
        inserts(tree[now].r,mid+1,r,pos,w);
       }
    }
    int lowbit(int x){
       return x&(-x);
    }
    void add(int x,int w){
       for(int i=x;i<=n;i+=lowbit(i)){
           inserts(root[i],1,n,a[x],w);
       }
    }
    int query(int l,int r,int ql,int qr){
        if (r<ql || l>qr){
            return 0;
        }
        int tmpl[1000],tmpr[1000];
        int s=0,mid=(l+r)>>1,sum=0;
        for(int i=1;i<=cnt1;i++)s-=tree[trl[i]].cnt,tmpl[i]=trl[i];
        for(int i=1;i<=cnt2;i++)s+=tree[trr[i]].cnt,tmpr[i]=trr[i];
        if (ql<=l && r<=qr){
             return s;
        }
        if (mid>=ql){
            for (int i=1;i<=cnt1;i++){
                trl[i]=tree[tmpl[i]].l;
            }
            for (int i=1;i<=cnt2;i++){
                trr[i]=tree[tmpr[i]].l;
            }
            sum+=query(l,mid,ql,qr);
        }
        if (mid<qr){
            for (int i=1;i<=cnt1;i++){
                trl[i]=tree[tmpl[i]].r;
            }
            for (int i=1;i<=cnt2;i++){
                trr[i]=tree[tmpr[i]].r;
            }
            sum+=query(mid+1,r,ql,qr);
        }
        return sum;
    }
    int main(){
      int m;
      scanf("%d%d",&n,&m);
        cnt=cnt1=cnt2=0;
      for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if (a[i]!=a[i-1])add(i,1);
      }
      int op;
      int pos,v,l,r,x,y;
      while(m--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d",&pos,&v);
            if (a[pos]==v)continue;
            if(a[pos]!=a[pos-1]){
                add(pos,-1);
            }
            if(a[pos]==a[pos+1]){
                add(pos+1,1);
            }else if (a[pos+1]==v){
                add(pos+1,-1);
            }
            a[pos]=v;
            if (a[pos]!=a[pos-1]){
                add(pos,1);
            }
        }else {
            scanf("%d%d%d%d",&l,&r,&x,&y);
            cnt1=cnt2=0;
            int f=0;
            for (int i=l;i;i-=lowbit(i)){
                trl[++cnt1]=root[i];
            }
            for (int i=r;i;i-=lowbit(i)){
                trr[++cnt2]=root[i];
            }
            if (a[l]>=x && a[l]<=y)f++;
            printf("%d
    ",query(1,n,x,y)+f);
        }
      }
      return 0;
    }

     CDQ分治,分成三维偏序问题,第一维时间,也就是构造和询问顺序,第二维度坐标,第三维度值域,按照三维偏序,进行处理,把询问排序即可。

       

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    const int maxx = 4e5+6;
    const int maxn=2e5+9;
    struct node{
      int op,f,l,x,id;
      ///操作类型 增加值 位置 值域 答案编号
      node(){}
      node(int op,int f,int l,int x):op(op),f(f),l(l),x(x){};
      node(int op,int f,int l,int x,int id):op(op),f(f),l(l),x(x),id(id){};
      bool operator < (const node &s) const{
          return l<s.l;
      }
    }q[maxx*2];
    int a[maxx];
    int ans[maxx];
    int sum[maxx];
    int n;
    ///BIT部分
    int lowbit(int x){
       return x&(-x);
    }
    void add(int x,int w){
       for (int i=x;i<=maxn;i+=lowbit(i)){
           sum[i]+=w;
       }
       return ;
    }
    int getsum(int x){
       int s=0;
       for (int i=x;i;i-=lowbit(i)){
           s+=sum[i];
       }
       return s;
    }
    void cdq(int l,int r){
        if (l==r){
            return;
        }
        int mid=(l+r)>>1;
        cdq(l,mid);
        cdq(mid+1,r);
        sort(q+l,q+mid+1);
        sort(q+mid+1,q+r+1);
        int i=l,j=mid+1;
        while(i<=mid && j<=r){
            ///右边的操作对左边没有影响
            if (q[j].op==1){
               j++;continue;
            }
            while(i<=mid && q[i].l<=q[j].l){
                ///左边的询问对右边没有影响
                if (q[i].op==1)add(q[i].x,q[i].f);
                i++;
            }
            ///查询小于等于q[j].x的个数,比乘以操作类型
            ans[q[j].id]+=q[j].f*getsum(q[j].x);
            j++;
        }
        while(j<=r){
            if (q[j].op==1){j++;continue;}
            ans[q[j].id]+=q[j].f*getsum(q[j].x);
            j++;
        }
        i=l,j=mid+1;
        /**清空**/
        while(i<=mid && j<=r){
            if (q[j].op==1){
               j++;
               continue;
            }
            while(i<=mid && q[i].l<=q[j].l){
                if (q[i].op==1)add(q[i].x,-q[i].f);
                i++;
            }
            j++;
        }
    }
    int main(){
      int m;
      scanf("%d%d",&n,&m);
      memset(ans,0,sizeof(ans));
      int tot=0;
      ///当成插入
      for (int i=1;i<=n;i++){
         scanf("%d",&a[i]);
         if (a[i]!=a[i-1])q[++tot]=node(1,1,i,a[i]);
      }
      int pos,v,l,r,x,y,op;
      int cnt=0;
      for (int i=1;i<=m;i++){
          scanf("%d",&op);
          if (op==1){
             scanf("%d%d",&pos,&v);
             if (a[pos]==v)continue;
             ///如果修改位置不等于前面一个,那么这个点是左端点,去掉需要在pos删除值a[pos]
             if (a[pos]!=a[pos-1]){
                 q[++tot]=node(1,-1,pos,a[pos]);
             }
             ///如果当前值是等前面一个值的,那么当前位置修改后,后面一个值,变成左端点,所以+1
             if (a[pos]==a[pos+1]){
                 q[++tot]=node(1,1,pos+1,a[pos+1]);
             ///如果后一个位置和前面一个值不同,代表右边一个是左端点,但是如果加入的值是等于这个值,那么需要减去
             }else if (a[pos+1]==v){
                 q[++tot]=node(1,-1,pos+1,a[pos+1]);
             }
             a[pos]=v;
             ///修改以后,如果不等于前面一个值,那么还需要更新
             if (a[pos]!=a[pos-1])
             q[++tot]=node(1,1,pos,a[pos]);
          }else {
             cnt++;
             scanf("%d%d%d%d",&l,&r,&x,&y);
             if (a[l]<=y && a[l]>=x){
                ans[cnt]=1;
             }
             l++;
             if (l>r)continue;
             ///把二维区间询问拆出来
             ///询问<=r && <=y的个数
             q[++tot]=node(2,1,r,y,cnt);
             ///询问<=y && <=l-1的个数
             if (l>1)q[++tot]=node(2,-1,l-1,y,cnt);
             ///询问<=x-1 && <=r的个数
             if (x>1)q[++tot]=node(2,-1,r,x-1,cnt);
             ///询问<=x-1 && <=l-1的个数
             if (l>1 && l>1)q[++tot]=node(2,1,l-1,x-1,cnt);
          }
      }
      cdq(1,tot);
      for (int i=1;i<=cnt;i++){
        cout<<ans[i]<<endl;
      }
      return 0;
    }
  • 相关阅读:
    【剑指Offer-循环和递归】面试题10.4:矩形覆盖
    【剑指Offer-循环和递归】面试题10.3:变态跳台阶
    【剑指Offer-面试案例】面试题66:把字符串转为整数
    【剑指Offer-发散思维能力】面试题66:构建乘积数组
    【剑指Offer-发散思维能力】面试题65:不用加减乘除做加法
    【剑指Offer-发散思维能力】面试题64:求1+2+...+n
    【剑指Offer-抽象建模能力】面试题62:圆圈中最后剩下的数字
    【剑指Offer-抽象建模能力】面试题61:扑克牌中的顺子
    【剑指Offer-知识迁移能力】面试题59:滑动窗口的最大值
    【剑指Offer-知识迁移能力】面试题58.2:左旋转字符串
  • 原文地址:https://www.cnblogs.com/bluefly-hrbust/p/11516694.html
Copyright © 2011-2022 走看看