zoukankan      html  css  js  c++  java
  • CDQ学习笔记

    想说的话

    其实我也不是很会,写这样一篇笔记送给未来的自己

    -----------------------------------------------------------------------------(NOIP2020)前一天,(xxbbkk)

    算法思想

    (CDQ)分治是分治的一种,本着分而治之的思想,用一半来算另一半的贡献。可以解决偏序问题。(蒟蒻只会三维以下)。

    例题一:逆序对问题

    用树状数组或归并排序的思想就可以做,是一个二维偏序问题。用权值树状数组。

    这里是归并的。

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[500010],ans,a1[500010],b1[500010];
    inline void msort(int l,int r){
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	msort(l,mid),msort(mid+1,r);
    	for(int i=l;i<=mid;i++) a1[i]=a[i];
    	for(int i=mid+1;i<=r;i++) b1[i]=a[i];
    	int tot=l-1,l1=l,l2=mid+1;
    	while(l1<=mid&&l2<=r){
    		if(a1[l1]>b1[l2]){
    			a[++tot]=b1[l2];
    			ans+=mid-l1+1;
    			l2++;
    		}
    		else{
    			a[++tot]=a1[l1];
    			l1++;
    		}
    	}
    	while(l1<=mid) a[++tot]=a1[l1++];
    	while(l2<=r) a[++tot]=b1[l2++];
    	return;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	msort(1,n);
    	printf("%d
    ",ans);
    	return 0;
    }
    
    

    这里是两种方法选其一,而三维偏序就是把他们结合起来。

    例题二:三维偏序问题

    Lougu[P3810]

    先以(a)为第一关键字排序,解决掉一维。

    向下分治时以(b)为第二关键字排序,再解决掉一维。在这里算的时候,我们只算前面那一半与后面那一半的组合产生的贡献,也就是跨越中点的贡献,这是分治的基本原则。

    计算时用两个指针(l_1,l_2)(l_1)遍历前面的一半,(l_2)遍历后面的一半,此时我们保证了前边一半的(a)小于后面一半的(a),而两半内部的(b)是有序的,所以只要后一半的位置(l_2)(b_{l_2})刚好大于前一半的某个位置(l_1)(b_{l_1}),就可以把(l_1)之前所有的(c)的值加入权值树状数组中,在权值树状数组中查询(c_{l_2})的前缀和。

    这里看代码清楚些。

    inline void CDQ(int l,int r){
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	CDQ(l,mid),CDQ(mid+1,r);
    	sort(v+l,v+mid+1,cmp2),sort(v+mid+1,v+r+1,cmp2);
    	int l1=l,l2;
    	for(l2=mid+1;l2<=r;l2++){
          //移动指针l1,找到那个刚好大于的位置pos1,并在权值线段中插入遍历到的c
    		while(v[l1].b<=v[l2].b&&l1<=mid){
    			add(v[l1].c,v[l1].cnt);
    			l1++;
    		}
          //累加答案,把权值树状数组中小于(此时的c_l2)的数的个数加上
    		v[l2].ans+=sum(v[l2].c);
    	}
      //消除这一层的改变,还原树状数组
    	for(l2=l;l2<l1;l2++) add(v[l2].c,-v[l2].cnt);
    	return;
    }
    

    还有,如果有完全相同的一组(a,b,c), 就有可能算不到贡献。

    所以我们要去重,统计个数。

    code

    #include<bits/stdc++.h>
    #define N (100010)
    using namespace std;
    struct xbk{int a,b,c,cnt,ans;}x[N],v[N];
    int n,m,k,c[200010],fa[200010];
    inline int read(){
    	int w=0;
    	char ch=getchar();
    	while(ch>'9'||ch<'0') ch=getchar();
    	while(ch>='0'&&ch<='9'){
    		w=(w<<3)+(w<<1)+(ch^48);
    		ch=getchar();
    	}
    	return w;
    }
    inline int lb(int x){return x&(-x);}
    inline bool cmp1(xbk aa,xbk bb){
    	if(aa.a!=bb.a) return aa.a<bb.a;
    	else if(aa.b!=bb.b) return aa.b<bb.b;
    	else return aa.c<bb.c;
    }
    inline bool cmp2(xbk aa,xbk bb){
    	if(aa.b!=bb.b) return aa.b<bb.b;
    	else return aa.c<bb.c;
    }
    inline void add(int x,int g){
    	for(int i=x;i<=k;i+=lb(i)) c[i]+=g;
    }
    inline int sum(int x){
    	int res=0;
    	for(int i=x;i;i-=lb(i)) res+=c[i];
    	return res;
    }
    inline void pre(){
    	int now=0;
    	sort(x+1,x+1+n,cmp1);
    	for(int i=1;i<=n;i++){
    		now++;
    		if(x[i].a!=x[i+1].a||x[i].b!=x[i+1].b||x[i].c!=x[i+1].c){
    			v[++m].a=x[i].a;
    			v[m].b=x[i].b,v[m].c=x[i].c;
    			v[m].ans=0,v[m].cnt=now;
    			now=0;
    		}
    	}
    	return;
    }
    inline void CDQ(int l,int r){
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	CDQ(l,mid),CDQ(mid+1,r);
    	sort(v+l,v+mid+1,cmp2),sort(v+mid+1,v+r+1,cmp2);
    	int l1=l,l2;
    	for(l2=mid+1;l2<=r;l2++){
    		while(v[l1].b<=v[l2].b&&l1<=mid){
    			add(v[l1].c,v[l1].cnt);
    			l1++;
    		}
    		v[l2].ans+=sum(v[l2].c);
    	}
    	for(l2=l;l2<l1;l2++) add(v[l2].c,-v[l2].cnt);
    	return;
    }
    int main(){
    	n=read(),k=read();
    	for(int i=1;i<=n;i++)
    		x[i].a=read(),x[i].b=read(),x[i].c=read();
    	pre();
    	CDQ(1,m);
    	for(int i=1;i<=m;i++) fa[v[i].ans+v[i].cnt-1]+=v[i].cnt;
    	for(int i=0;i<n;i++) printf("%d
    ",fa[i]);
    	return 0;
    }
    

    例题三:动态逆序对

    LouguP3157

    待修改的逆序对,用权值作第一维(a),位置作第二维(b),结束时间做第三维(c)

    元素(y)对于元素(x)是逆序对的条件为:

    (x.a<y.a)(x.b > y.b)(x.c>y.c)

    或:

    (x.a>x.b)(x.b<y.b)(x.c>y.c)

    就变成两个三维偏序啦♪(*)

    code

    #include<bits/stdc++.h>
    #define N (100010)
    #define K (200010)
    #define ll long long
    using namespace std;
    struct xbk{int a,b,c,cnt;ll ans;}x[N],v[N];
    int n,m,kk,tot,cnt,now,c[K],re[N];
    ll fi,ans[N];
    inline int lb(int x){return x&(-x);}
    inline int read(){
    	int w=0;
    	char ch=getchar();
    	while(ch>'9'||ch<'0') ch=getchar();
    	while(ch>='0'&&ch<='9'){
    		w=(w<<3)+(w<<1)+(ch^48);
    		ch=getchar();
    	}
    	return w;
    }
    inline void add(int x,int kkk){
    	for(int i=x;i<=n;i+=lb(i)) c[i]+=kkk;
    	return;
    }
    inline ll sum(int x){
    	int res=0;
    	for(int i=x;i>0;i-=lb(i)) res+=c[i];
    	return res;
    }
    inline bool cmp1(xbk aa,xbk bb){
    	if(aa.a!=bb.a) return aa.a>bb.a;
    	else if(aa.b!=bb.b) return aa.b<bb.b;
    	else return aa.c<bb.c;
    }
    inline bool cmp2(xbk aa,xbk bb){
    	if(aa.b!=bb.b) return aa.b<bb.b;
    	else return aa.c<bb.c;
    }
    inline bool cmp3(xbk aa,xbk bb){
    	if(aa.b!=bb.b) return aa.b>bb.b;
    	else return aa.c<bb.c;
    }
    inline void CDQ(int l,int r){
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	CDQ(l,mid),CDQ(mid+1,r);
    	sort(v+l,v+mid+1,cmp3),sort(v+mid+1,v+r+1,cmp3);
    	int i=l,j=mid+1;
    	for(j=mid+1;j<=r;j++){
    		while(v[i].b>v[j].b&&i<=mid){
    			add(v[i].c,v[i].cnt);
    			i++;
    		}
    		v[j].ans+=sum(v[j].c);
    	}
    	for(int k=l;k<i;k++) add(v[k].c,-v[k].cnt);
    	sort(v+l,v+mid+1,cmp2),sort(v+mid+1,v+r+1,cmp2);
    	i=l;
    	for(j=mid+1;j<=r;j++){
    		while(v[i].b<v[j].b&&i<=mid){
    			add(v[i].c,v[i].cnt);
    			i++;
    		}
    		v[j].ans+=sum(n)-sum(v[j].c);
    	}
    	for(int k=l;k<i;k++) add(v[k].c,-v[k].cnt);
    	return;
    }
    int main(){
    	n=read(),m=read();
    	kk=n;
    	for(int i=1;i<=n;i++){
    		v[i].b=read(),v[i].c=i;
    		re[v[i].b]=i;
    		v[i].a=1e9,v[i].cnt=1,v[i].ans=0;
    	}
    	for(int i=1;i<=m;i++){
    		int xx=read();
    		v[re[xx]].a=i;
    	}
    	sort(v+1,v+1+n,cmp1);
    	CDQ(1,n);
    //	for(int i=1;i<=n;i++){
    //		printf("%d %d %d %d
    ",v[i].a,v[i].b,v[i].c,v[i].ans);
    //	}
    	
    	for(int i=1;i<=n;i++){
    		fi+=v[i].ans;
    		if(v[i].a!=1e9) ans[v[i].a]=v[i].ans;
    	}
    	for(int i=1;i<=m;i++) printf("%lld
    ",fi-=ans[i-1]);
    	return 0;
    }
    

    未完待续❀

  • 相关阅读:
    CSOL大灾变移植记录
    游戏设计技巧——对象信息封装
    Filament初探,全场景性能测试
    Godot从编辑器创建自定义场景类型对象
    Python中面向对象编程和内置方法的使用解析【转】
    python中使用xlrd、xlwt操作excel表格详解【转】
    Python第三方库xlrd/xlwt的安装与读写Excel表格【转】
    python实现以及所有排序大总结【转】
    Python下异常、模块、文件、数据储存-json等知识讲解【转】
    python文件系统详细介绍【转】
  • 原文地址:https://www.cnblogs.com/xxbbkk/p/14086163.html
Copyright © 2011-2022 走看看