zoukankan      html  css  js  c++  java
  • 偏序问题及CDQ分治详解

    CDQ用来解决分治时左半部分对右半部分造成影响的问题。

    CDQ分治的经典问题是三维偏序问题。

    要想解决三维偏序问题,首先你要知道什么是偏序。(废话

    一维偏序:

    给出直线上的n个点,问有多少对点满足xi<=xj

    对于这个问题,直接排序就可以了。

    二维偏序:

    给定平面内的n个点,问有多少对点满足xi<=xj且yi<=yj

    这是个经典的树状数组问题,相信学过树状数组的人一定都做过·一道叫做数星星的题,这道题就是经典的二维偏序问题,并不需要二维数组,我们可以通过按x坐标为第一关键字排序,从而消除x坐标给答案带来的影响。然后我们用一个树状数组维护前缀和,记录之前有多少点的y坐标比该点小,由于在之前的x坐标一定比较小,因此只要保证y坐标即可。

    三维偏序:

    给出空间内的n个点,问有多少点对满足xi<=xj且yi<=yj且zi<=zj

    树套树???码量++,空间++

    在不强制在线的情况下,我们完全可以使用CDQ分治这种东西来简化一层树结构,因此三维偏序问题实际上可以这样处理:

    1)仿照二位偏序问题,先给x排序,消除其影响。

    2)使用CDQ分治,消除y的影响

    3)使用数组维护z的前缀,统计答案。

    1)、3)都是二维偏序的正常步骤,下面重点来讲讲CDQ分治

    前边已经讲过了,CDQ分治要处理左区间对右区间的贡献问题。

    其实很简单,我们把每一步操作分成三步:

    1)递归处理左区间;2)递归处理右区间;3)处理左区间对右区间的贡献。

    在三维偏序问题中,之所以不考虑右区间对左区间的贡献,是因为按x排序了,因此右区间一定不会对左区间造成贡献。

    然后我们直接就按y排序,然后看一个东西是在左区间还是右区间,是左区间就放进一个树状数组中,否则直接求贡献。

    因为CDQ直接分治到最小的单位,也就是一个点,因此可以保证所有答案都是没有遗漏的。

    为了保证答案的不重复性,我们可以在求完贡献后把树状数组内贡献再撤消;

    例题:洛谷P3810:陌上花开

    参考代码如下:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<queue>
     5 #define N 500005
     6 #define lowbit(x) x&-x
     7 using namespace std;
     8 int read()
     9 {
    10     int x=0,f=1;char ch=getchar();
    11     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    12     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    13     return x*f;
    14 }
    15 struct node{int x,y,z,id;}a[N];
    16 bool cmpa(node a,node b)
    17 {
    18     if(a.x!=b.x)return a.x<b.x;
    19     if(a.y!=b.y)return a.y<b.y;
    20     return a.z<b.z;
    21 }
    22 bool cmpb(node a,node b)
    23 {
    24     if(a.y!=b.y)return a.y<b.y;
    25     if(a.x!=b.x)return a.x<b.x;
    26     return a.z<b.z;
    27 }
    28 int n,k,tot,c[500005],b[500005],f[500005],s,j,block[500005];
    29 void modify(int x,int m){for(;x<=k;x+=lowbit(x))c[x]+=m;}
    30 int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
    31 void cdq(int l,int r)
    32 {
    33     if(l==r)return;
    34     int mid=(l+r)/2;
    35     cdq(l,mid);cdq(mid+1,r);
    36     sort(a+l,a+r+1,cmpb);
    37     for(int i=l;i<=r;i++)(a[i].x<=mid)?modify(a[i].z,1),s=1:b[a[i].id]+=query(a[i].z);
    38     for(int i=l;i<=r;i++)if(a[i].x<=mid)modify(a[i].z,-1);
    39 }
    40 int main()
    41 {    
    42     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    43     cin>>n>>k;//n=read();k=read();
    44     for(int i=1;i<=n;i++)
    45     {
    46         cin>>a[i].x>>a[i].y>>a[i].z;//=read();a[i].y=read();a[i].z=read();
    47         a[i].id=i;
    48     }
    49     sort(a+1,a+1+n,cmpa);
    50     for(int i=1;i<=n;)
    51     {
    52         j=i+1;
    53         while(j<=n&&a[i].x==a[j].x&&a[i].y==a[j].y&&a[i].z==a[j].z)j++;
    54         while(i<j)block[a[i].id]=a[j-1].id,i++;
    55     }
    56     for(int i=1;i<=n;i++)a[i].x=i;
    57     cdq(1,n);
    58     for(int i=1;i<=n;i++)f[b[block[a[i].id]]]++; 
    59     for(int i=0;i<n;i++)printf("%d
    ",f[i]);
    60     return 0;
    61 }
    三位偏序模板代码
  • 相关阅读:
    js 修改 title keywords description
    添加第一次进入网站动画
    懒加载
    一些正则
    对图片进行剪切,保留原始比例
    JS判断是否是微信页面,判断手机操作系统(ios或android)并跳转到不同下载页面
    判断网页是否再微信内置浏览器打开
    数字转汉字大写
    java 反射机制 之 getConstructor获取有参数构造函数 然后newInstance执行有参数的构造函数
    web实训项目(快递e栈)-----04项目实现的基本流程
  • 原文地址:https://www.cnblogs.com/szmssf/p/11481094.html
Copyright © 2011-2022 走看看