• [用CDQ分治解决区间加&区间求和]【习作】


    【前言】

    作为一个什么数据结构都不会只会CDQ分治和分块的蒟蒻,面对区间加&区间求和这么难的问题,怎么可能会写线段树呢

    于是,用CDQ分治解决区间加&区间求和这篇习作应运而生


    【Part.I】区间加&区间求和的数据结构做法

    【一】线段树

    裸题...

    1141ms

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    #define lc x<<1
    #define rc x<<1|1
    #define mid ((l+r)>>1)
    #define lson lc, l, mid
    #define rson rc, mid+1, r
    typedef long long ll;
    const int N=1e5+5;
    inline ll read(){
        char c=getchar();ll x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int n,Q,op,x,y;
    struct SegmentTree{
        struct meow{ ll sum, tag; } t[N<<2];
        inline void paint(int x,int l,int r,ll v){
            t[x].tag+= v;
            t[x].sum+= (r-l+1)*v;
        }
        inline void pushDown(int x,int l,int r){
            if(t[x].tag){
                paint(lson, t[x].tag);
                paint(rson, t[x].tag);
                t[x].tag=0;
            }
        }
        void build(int x,int l,int r){
            if(l==r) t[x].sum=read();
            else{
                build(lson);
                build(rson);
                t[x].sum=t[lc].sum+t[rc].sum;
            }
        }
        void Add(int x,int l,int r,int ql,int qr,ll v){
            if(ql<=l && r<=qr) paint(x,l,r,v);
            else{
                pushDown(x,l,r);
                if(ql<=mid) Add(lson, ql, qr, v);
                if(mid<qr)  Add(rson, ql, qr, v);
                t[x].sum=t[lc].sum+t[rc].sum;
            }
        }
        ll Que(int x,int l,int r,int ql,int qr){
            if(ql<=l && r<=qr) return t[x].sum;
            else{
                pushDown(x,l,r);
                ll ans=0;
                if(ql<=mid) ans+=Que(lson, ql, qr);
                if(mid<qr)  ans+=Que(rson, ql, qr);
                return ans;
            }
        }
    }S;
    int main(){
        //freopen("in","r",stdin);
        n=read(); Q=read();
        S.build(1,1,n);
        while(Q--){
            op=read();x=read();y=read();
            if(op==1) S.Add(1,1,n,x,y,read() );
            else printf("%lld
    ", S.Que(1,1,n,x,y) );
        }
    }
    SegmentTree

    【二】树状数组

    考虑每个位置的贡献,维护$a[i]$和$i*a[i]$

    477ms

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    inline ll read(){
        char c=getchar();ll x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int n,Q,op,x,y;
    struct meow{
        ll c[N];
        inline void add(int p,ll v) {for(;p<=n;p+=p&-p) c[p]+=v;}
        inline ll sum(int p) {ll re=0; for(;p;p-=p&-p) re+=c[p]; return re;}
        inline ll sum(int l,int r) {return sum(r)-sum(l-1);}
    }C1,C2;
    struct BinaryIndexTree{
        inline void Add(int l,int r,ll v){
            C1.add(l,v);   C1.add(r+1,-v);
            C2.add(l,l*v); C2.add(r+1,-(r+1)*v);
        }
        inline ll Que(int l,int r){
            return (r-l+1)*C1.sum(1,l) + (r+1)*C1.sum(l+1,r) - C2.sum(l+1,r);
        }
    }A;
    int main(){
    //    freopen("in","r",stdin);
        n=read(); Q=read();
        for(int i=1;i<=n;i++) A.Add(i,i,read() );
        while(Q--){
            op=read();x=read();y=read();
            if(op==1) A.Add(x,y,read() );
            else printf("%lld
    ", A.Que(x,y) );
        }
    }
    BinaryIndexTree

    【Part.II】区间加&区间求和的CDQ分治做法

    首先我们明确CDQ分治是什么 参见[偏序关系与CDQ分治]【学习笔记】

    用CDQ分治解决单点加&区间求和  区间加&单点求值 是很容易的,但要两个都是区间就不太方便了

    但经过两个多小时的研究,终于做出来啦

    区间加&区间求和可以算是二维偏序问题,可以只用排序和CDQ分治不借助任何数据结构解决

    首先把修改和询问都拆成两个

    我们对时间排序,对位置进行CDQ分治

    问题在于对于$[l,r]$这样一个加操作,$r<p$的当然很方便了,但对$l le p le r$的每个$p$贡献都是不一样的

    一开始困扰了我好久

    突然想到可以维护一个$val$表示当前每在位置上移动一个距离总体的贡献应该改变多少,再维护一个$last$表示上一个位置

    然后更新当前的贡献时用$val$乘移动的距离就可以了

    其实本质就是得到了计算$[l,mid]$中修改的贡献后每一个位置的值应该是多少

    问题解决!撒花

    1251ms 时间垫底了.....

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    inline ll read(){
        char c=getchar();ll x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int n,Q,m,op,l,r; 
    ll a[N],v,ans[N];
    struct meow{
        int x,type,qid; ll v; 
        meow(){}
        meow(int b,int c,int d,ll e):x(b),type(c),qid(d),v(e){}
        bool operator <(const meow &r) const {return x==r.x ? type<r.type : x<r.x;}
    }q[N<<2],t[N<<2];
    
    void CDQ(int l,int r){ 
        if(l==r) return ;
        int mid=(l+r)>>1;
        CDQ(l, mid); CDQ(mid+1, r);
        int i=l, j=mid+1, p=l;
        ll now=0, val=0; int last=0;
        while(i<=mid || j<=r){
            if(j>r || (i<=mid && q[i]<q[j]) ){
                now+=(q[i].x-last)*val; last=q[i].x;
                if(q[i].type!=0) val+=q[i].v;
                t[p++]=q[i++];
            }else{
                now+=(q[j].x-last)*val; last=q[j].x;
                if(!q[j].type) ans[ q[j].qid ]+= now*q[j].v ; 
                t[p++]=q[j++];
            }
        }
        for(int i=l;i<=r;i++) q[i]=t[i];
    }
    
    int main(){
        freopen("in","r",stdin);
        n=read(); Q=read(); int p=0;
        for(int i=1;i<=n;i++) v=read(), q[++m]=meow(i-1,-1,0,v), q[++m]=meow(i,1,0,-v);
    
        for(int i=1;i<=Q;i++){
            op=read(); l=read(); r=read(); 
            if(op==1) v=read(), q[++m]=meow(l-1,-1,0,v), q[++m]=meow(r,1,0,-v);
            else p++, q[++m]=meow(l-1,0,p,-1), q[++m]=meow(r,0,p,1); 
        }
        CDQ(1,m);
        for(int i=1;i<=p;i++) printf("%lld
    ",ans[i]);
    }
    CDQ分治

    【Part.III】应用

    这玩意常数又大又需要离线有什么用啊

    1.我们可以出题坐标特别大强制线段树来离线离散化

    2.可以推广到一系列区间修改与查询问题

  • 相关阅读:
    [iOS Animation]-CALayer 图像IO
    weblogic集群
    mysql相关函数及用法
    mysql导出导入数据库
    java多线程实现复制功能并统计进度
    java多线程模拟实现12306售票
    Oracle中的常用数据类型
    jdbc连接mysql和oracle数据库-java
    将字符串插入到光标指定位置-js
    二维数组模拟实现酒店管理系统-java
  • 原文地址:https://www.cnblogs.com/candy99/p/cdqrange.html
Copyright © 2020-2023  润新知