zoukankan      html  css  js  c++  java
  • [11/07/19] CDQ学习笔记

    CDQ算法据说是对付离线操作下修改查询问题的重要工具,是基于分治算法的降维操作。

      CDQ算法核心思想只有三步:递归处理左区间,处理左区间对右区间的影响,递归处理右区间。原理的话按照我这个蒟蒻的理解就是在完成任意查询操作前完成此查询操作前的修改操作,并且因为是基于分治算法,一定严格按照顺序完成查询操作,从而保证前面的操作不受后面操作的影响,保证了正确性。优化的地方则在于可以优雅的嵌套以维护多维的大小关系,比如保证多元组 ((a,b,...,z)) 满足 (a_i<a_j&&b_i<b_j&&...&&z_i<z_j) 等等,还可以用来优化 (DP)

      适用范围 一般是要离线并且有明确线性操作顺序并且操作有修改和查询并且能抽象出多元组模型。(有些问题是求区间值,一般也作为多元组中一个参数)例如查询满足 包含/有明确上下限 的问题

      操作过程 (以三元组为例)

      我们得到了一堆三元组 (f_i.(a,b,c))与一个任务: 对于每个 (i) ,求出使 (f_i.a>f_j.a)(f_i.b>f_j.b)(f_i.c>f_j.c)(j) 的个数 (K_i),然后求出对于每个 (d in [0,n)),有几个 (K_i) 与之相等。

      先按关键字 (a) 排序.

      然后分治,把当前区间 ([l,r]) 分成 ([l,mid])([mid+1,r]),分别按 (b) 为关键字排序.(如代码,可以直接归并排!)记录此数来自右区间或左区间。

      (Tip1) 不要有什么乱七八糟的想法。比如说你认为如果这样排序 (a) 就乱掉了,你是对的,但我们只考虑左区间与右区间的大小关系,很明显右区间的每一个 (a) 都大于左区间的 (a) 。又很明显分治到一定程度以后,除了最前面也就是 (a) 最小的那个 (f_i) ,其他每个 (f_i) 都可能到当前子问题的右区间。原理就是在以 (a) 排完序后只有当前 (i) 前的数可能对当前的 (i) 答案有贡献。

      然后如果问题有很多很多维,重复上述过程直到剩下最后一维。

      然后对于最后一维本来应该套个树状数组或者其他数据结构,但是其实可以直接再套一层CDQ求答案。其实这层CDQ也是以归并排序的形式 (for) 循环的,但并不需要归并,所以代码里会有废话(其实也不是废话,但是只要把 (.ans) 转移即可)。归并时若当前选中的多元组来自在每一维均来自左区间,指针 (cnt++),若均来自右区间,则当前 (i) 的答案加上 (cnt)

      (Tip2) 才不是我打不来树状数组,注意去重,如果有重复多元组合并,当前 (i) 的答案可以直接 (++)

      (Tip3) 由于会有一大堆的排序,我们必须把 (ans) 定为 (int *) 类型以保证地址不变,也就是干脆直接关联 (f_i.ans)(K_i)

    #include<bits/stdc++.h>
    using namespace std;
    struct note{
        int x,y,z;
        bool flg;
        int *ans;
    }a[101010],b[101010],c[101010];
    int n,k,t[101010],ans[101010];
    inline bool cmp(note a,note b){    //千万不要用三目运算符,除非你会
        if(a.x!=b.x) return a.x<b.x;
        if(a.y!=b.y) return a.y<b.y;
        return a.z<b.z;
    }
    inline int read(){
        int ret=0,f=1;char ch=getchar();
        while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
        while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
        return ret*f;
    }
    inline void st2(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st2(l,mid);st2(mid+1,r);
        int j=l,k=mid+1,cnt=0;
        for(int i=l;i<=r;i++){
            if((k>r||b[j].z<=b[k].z)&&j<=mid){          //z为关键字归并 
                c[i]=b[j++];                            //废话 
                cnt+=c[i].flg;                          //只要 cnt+=b[j].flg,j++;即可 
            }
            else{
                c[i]=b[k++];                            //废话 
                if(!c[i].flg)*c[i].ans+=cnt;            //只要 if(!b[k].flg)*b[k].ans+=cnt;k++;即可 
            }
        }
        for(int i=l;i<=r;i++)b[i]=c[i];
    }
    inline void st1(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st1(l,mid);st1(mid+1,r);
        int j=l,k=mid+1;
        for(int i=l;i<=r;i++){
            if((k>r||a[j].y<=a[k].y)&&j<=mid)b[i]=a[j++],b[i].flg=1;          //y为关键字归并入左区间的数 
            else b[i]=a[k++],b[i].flg=0;            //归并入右区间的数 
        }
        for(int i=l;i<=r;i++)a[i]=b[i];
        st2(l,r);
    }
    int main(){
        n=read(),k=read();
        for(int i=1;i<=n;i++)a[i].x=read(),a[i].y=read(),a[i].z=read();            //读入 
        for(int i=1;i<=n;i++)a[i].ans=&ans[i],ans[i]=0;sort(a+1,a+n+1,cmp);        //统一地址及排序 
        for(int i=n-1;i>=1;i--)if(a[i].x==a[i+1].x&&a[i].y==a[i+1].y&&a[i].z==a[i+1].z)*a[i].ans=*a[i+1].ans+1;     //去重 
        st1(1,n);              //分治 
        for(int i=1;i<=n;i++)t[ans[i]]++;       //桶排 
        for(int i=0;i<n;i++)printf("%d
    ",t[i]);
        return 0;
    }
    

      不过像 Mokia 之类有修改操作且修改数据非常数的最好还是用数据结构维护....也不是说CDQ不行,只是改起来烦。

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	int ch=getchar(),res=0,f=1;
    	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    	return res*f;
    }
    const int N=500005;
    struct ask{int l,r,a,b;}q1[N],a[N];
    int s,n,tot,cnt,ans[N],tr[2000000];
    inline int lowbit(int x){return (x&(-x));}
    inline void update(int a,int k){for(;a<=n;a+=lowbit(a))tr[a]+=k;}
    inline int query(int a,int res=0){for(;a;a-=lowbit(a))res+=tr[a];return res;}
    inline bool cmp(ask a,ask b){if(a.l!=b.l)return a.l<b.l;return a.r<b.r;}
    inline void cdq(int l,int r){
    	if(l==r)return;int mid=l+r>>1;
    	cdq(l,mid),cdq(mid+1,r);
    	sort(a+l,a+mid+1,cmp);
    	sort(a+mid+1,a+r+1,cmp);
    	int i=l;
    	for(int j=mid+1;j<=r;j++){
    		for(;i<=mid&&a[i].l<=a[j].l;i++)
    			if(a[i].b==1)update(a[i].r,a[i].a);
    		if(a[j].b==2)ans[a[j].a]+=query(a[j].r);
    	}
    	for(int j=l;j<i;j++)if(a[j].b==1)update(a[j].r,-a[j].a);
    }
    int main(){
    	s=read(),n=read();
    	int b=read();
    	while(b!=3){
    		if(b==1){
    			a[++tot].l=read(),a[tot].r=read(),a[tot].a=read(),a[tot].b=1;
    		}
    		else{
    			int x1=read(),y1=read(),x2=read(),y2=read();
    			a[++tot].l=x2,a[tot].r=y2,a[tot].a=++cnt,a[tot].b=2;
    			a[++tot].l=x1-1,a[tot].r=y2,a[tot].a=++cnt,a[tot].b=2;
    			a[++tot].l=x2,a[tot].r=y1-1,a[tot].a=++cnt,a[tot].b=2;
    			a[++tot].l=x1-1,a[tot].r=y1-1,a[tot].a=++cnt,a[tot].b=2;
    		}
    		b=read();
    	}
    	cdq(1,tot);
    	for(int i=1;i<=cnt;i+=4){
    		cout<<(ans[i]-ans[i+1]-ans[i+2]+ans[i+3])<<'
    ';
    	}
    }
    

      附四维代码

    //四维陌上花开 
    #include<bits/stdc++.h>
    using namespace std;
    struct note{
        int x,y,z,cz;
        bool flg1,flg2;
        int *ans;
    }a[101010],b[101010],c[101010],d[101010];
    int m,n,k,t[101010],ans[101010];
    int X1,X2,Y1,Y2,Z1,Z2,tot;
    inline bool cmp(note a,note b){    //千万不要用三目运算符,除非你会
        if(a.x!=b.x) return a.x<b.x;
        if(a.y!=b.y) return a.y<b.y;
        if(a.z!=b.z) return a.z<b.z;
        return a.cz<b.cz;
    }
    inline int read(){
        int ret=0,f=1;char ch=getchar();
        while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
        while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
        return ret*f;
    }
    inline void st3(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st3(l,mid);st3(mid+1,r);
        int j=l,k=mid+1,cnt=0;
        for(int i=l;i<=r;i++){
            if((k>r||c[j].cz<=c[k].cz)&&j<=mid){
                d[i]=c[j++];
                cnt+=d[i].flg1&d[i].flg2;                           //如果每一次修改操作不一样的话可以写成cnt+=(d[i].flg1&d[i].flg2)*d[i].xiugaizhi; 
    			                                                    //如果不止一维改成cnt+=d[i].flg1&d[i].flg2&...&d[i].flgn;即可 
            }
            else{
                d[i]=c[k++];
                if(!d[i].flg1&&!d[i].flg2) *d[i].ans+=cnt;          //每一维都来自右区间 
            }
        }
        for(int i=l;i<=r;i++)c[i]=d[i];
    }
    inline void st2(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st2(l,mid);st2(mid+1,r);
        int j=l,k=mid+1;
        for(int i=l;i<=r;i++){
            if((k>r||b[j].z<=b[k].z)&&j<=mid)c[i]=b[j++],c[i].flg2=1;
            else c[i]=b[k++],c[i].flg2=0;
        }
        for(int i=l;i<=r;i++)b[i]=c[i];
        st3(l,r);
    }
    inline void st1(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st1(l,mid);st1(mid+1,r);
        int j=l,k=mid+1;
        for(int i=l;i<=r;i++){
            if((k>r||a[j].y<=a[k].y)&&j<=mid)b[i]=a[j++],b[i].flg1=1;
            else b[i]=a[k++],b[i].flg1=0;
        }
        for(int i=l;i<=r;i++)a[i]=b[i];
        st2(l,r);
    }
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].cz=read();            //读入 
    	for(int i=1;i<=n;i++) a[i].ans=&ans[i],ans[i]=0;sort(a+1,a+n+1,cmp);        //统一地址及排序 
    	st1(1,n);for(register int i=1;i<=n;i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    
    //HDU5126
    #pragma GCC optimize(2)
    #include<bits/stdc++.h>//STO MOD LargestJN Orz %%%%%%
    using namespace std;
    struct note{
        int x,y,z,cz;
        bool flg1,flg2;
        int *ans;
    }a[101010],b[101010],c[101010],d[101010];
    int m,n,k,t[101010],ans[101010];
    int X1,X2,Y1,Y2,Z1,Z2,tot;
    inline bool cmp(note a,note b){
        if(a.x!=b.x) return a.x<b.x;
        if(a.y!=b.y) return a.y<b.y;
        return a.z<b.z;
    }
    inline int read(){
        int ret=0,f=1;char ch=getchar();
        while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
        while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
        return ret*f;
    }
    inline void st3(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st3(l,mid);st3(mid+1,r);
        int j=l,k=mid+1,cnt=0;
        for(int i=l;i<=r;i++){
            if((k>r||c[j].z<=c[k].z)&&j<=mid){
                d[i]=c[j++];
                if(d[i].flg1&&d[i].flg2&&d[i].cz)cnt++;
            }
            else{
                d[i]=c[k++];
                if(!d[i].flg1&&!d[i].flg2&&!d[i].cz)*d[i].ans+=cnt;
            }
        }
        for(int i=l;i<=r;i++)c[i]=d[i];
    }
    inline void st2(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st2(l,mid);st2(mid+1,r);
        int j=l,k=mid+1;
        for(int i=l;i<=r;i++){
            if((k>r||b[j].y<=b[k].y)&&j<=mid)c[i]=b[j++],c[i].flg2=1;
            else c[i]=b[k++],c[i].flg2=0;
        }
        for(int i=l;i<=r;i++)b[i]=c[i];
        st3(l,r);
    }
    inline void st1(int l,int r){
        if(l==r)return;
        int mid=l+r>>1;
        st1(l,mid);st1(mid+1,r);
        int j=l,k=mid+1;
        for(int i=l;i<=r;i++){
            if((k>r||a[j].x<=a[k].x)&&j<=mid)b[i]=a[j++],b[i].flg1=1;
            else b[i]=a[k++],b[i].flg1=0;
        }
        for(int i=l;i<=r;i++)a[i]=b[i];
        st2(l,r);
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++){
        	k=read();
        	if(k==1)a[++m].x=read()+1,a[m].y=read()+1,a[m].z=read()+1,a[m].cz=1;
        	else {
        		X1=read(),Y1=read(),Z1=read();
        		X2=read(),Y2=read(),Z2=read();
        		X2++,Y2++,Z2++;t[++tot]=m+1;
        		a[++m].x=X2,a[m].y=Y2,a[m].z=Z2;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X1,a[m].y=Y2,a[m].z=Z2;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X2,a[m].y=Y1,a[m].z=Z2;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X2,a[m].y=Y2,a[m].z=Z1;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X1,a[m].y=Y1,a[m].z=Z2;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X1,a[m].y=Y2,a[m].z=Z1;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X2,a[m].y=Y1,a[m].z=Z1;
        		a[m].ans=&ans[m];ans[m]=0;
        		a[++m].x=X1,a[m].y=Y1,a[m].z=Z1;
        		a[m].ans=&ans[m];ans[m]=0;
    		}
    	}
        st1(1,m);
        for(int i=1;i<=tot;i++) printf("%d
    ",ans[t[i]]-ans[t[i]+1]-ans[t[i]+2]-ans[t[i]+3]+ans[t[i]+4]+ans[t[i]+5]+ans[t[i]+6]-ans[t[i]+7]);//千万千万注意,不要把t[i]+n写成t[i+n]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
        return 0;
    }
    
  • 相关阅读:
    html实现 省——市——区三级联动
    test
    JAVA课程设计——坦克大战
    Java MOOC-互评作业-流与文件
    DS博客作业06--图
    DS博客作业08--课程总结
    DS博客作业07--查找
    DS博客作业06--图
    DS博客作业05-树
    DS博客作业01--日期抽象数据类型设计与实现
  • 原文地址:https://www.cnblogs.com/alexiswithlucifer/p/11174267.html
Copyright © 2011-2022 走看看