【CDQ分治】 BZOJ3262 陌上花开

本文介绍了一种解决经典三维偏序问题的方法,采用CDQ分治算法处理x轴,y轴进行排序,z轴利用树状数组维护。通过示例程序详细展示了如何实现这一算法,并给出了解决方案的具体步骤。

题面在这里

最经典的三维偏序问题

x用CDQ分治,y排序,z树状数组维护

示例程序:

#include<cstdio>
#include<algorithm>
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int red(){
    int res=0,f=1;char ch=nc();
    while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=nc();}
    while ('0'<=ch&&ch<='9') res=res*10+ch-48,ch=nc();
    return res*f;
}

const int maxn=200005;
int n,m=2e5,num[maxn],ans[maxn];
struct data{
    int x,y,z,id;
    inline void read() {x=red();y=red();z=red();}
    inline bool operator==(const data&b)const{return x==b.x&&y==b.y&&z==b.z;}
}a[maxn],t[maxn];
inline const bool cmp(const data&a,const data&b){
    return a.x<b.x || a.x==b.x&&a.y<b.y || a.x==b.x&&a.y==b.y&&a.z<b.z;
}
inline const bool cmpy(const data&a,const data&b){
    return a.y<b.y||a.y==b.y&&a.id<b.id;
}
#define lowbit(x) ((x)&-(x))
int BIT[maxn];
inline void ist(int x,int w){
    for (;x<=m;x+=lowbit(x)) BIT[x]+=w;
}
inline int ask(int x){
    int res=0;
    for (;x;x-=lowbit(x)) res+=BIT[x];
    return res;
}
void CDQ(int l,int r){
    if (l==r) return;
    int mid=l+r>>1;
    CDQ(l,mid);CDQ(mid+1,r);
    for (int i=l;i<=r;i++){
        t[i]=a[i];
        if (i<=mid) t[i].id=0;
    }
    sort(t+l,t+r+1,cmpy);
    for (int i=l;i<=r;i++)
     if (!t[i].id) ist(t[i].z,1);else num[t[i].id]+=ask(t[i].z);
    for (int i=l;i<=r;i++)
     if (!t[i].id) ist(t[i].z,-1);
}
int main(){
    n=red();m=red();
    for (int i=1;i<=n;i++) a[i].read(),a[i].id=i;
    sort(a+1,a+1+n,cmp);
    for (int i=n-1,t=0;i;i--){
        if (a[i]==a[i+1]) t++;else t=0;
        num[a[i].id]=t;
    }
    CDQ(1,n);
    for (int i=1;i<=n;i++) ans[num[i]]++;
    for (int i=0;i<n;i++) printf("%d\n",ans[i]); 
    return 0;
}
### CDQ分治法的基本概念 CDQ分治是一种基于分治思想的算法策略,最初由陈丹琦引入国内算法竞赛领域,因此被称为CDQ分治。该方法的核心思想是将问题划分为若干子问题,并递归地解决这些子问题。在解决子问题的同时,处理左半部分对右半部分的影响,从而逐步构建最终解[^1]。 CDQ分治的关键特征在于其递归结构:首先递归处理左半区间和右半区间的子问题,随后处理左区间对右区间的影响。这种策略通常利用排序来制造单调性,从而降低计算复杂度[^2]。 ### CDQ分治法的工作原理 CDQ分治的工作流程可以分为以下三个步骤: 1. **划分**:将原始问题划分为两个子问题,通常是对数组进行二分,分别处理左半区间和右半区间。 2. **处理**:计算左半区间对右半区间的影响。这一过程通常涉及对数据进行排序,以利用单调性减少重复计算[^3]。 3. **合并**:递归地处理右半区间的问题,并将结果整合。 以归并排序求逆序对为例,在合并两个子区间的过程中,需要计算左边区间对右边区间的影响。具体来说,当从右子区间中取出一个元素时,统计左边区间中比该元素大的元素数量,从而得到逆序对的个数。这一过程体现了CDQ分治的核心思想[^3]。 ### CDQ分治法的应用场景 CDQ分治广泛应用于解决多维偏序问题,例如二维偏序三维偏序等。对于二维偏序问题,可以通过CDQ分治结合排序和树状数组来高效求解。在处理三维偏序问题时,CDQ分治通常与树状数组结合,以达到 $ O(n \log^2 n) $ 的时间复杂度[^5]。 此外,CDQ分治也常用于动态规划优化问题。例如,在求解最长递增子序列问题时,可以利用CDQ分治处理条件约束,例如 $ f_i = \max\{f_j + 1 \mid j < i, r_j \le a_i, a_j \le l_i\} $,其中 $ f_i $ 表示以第 $ i $ 个元素为结尾的最长子序列长度[^4]。 ### CDQ分治法的实现示例 以下是一个基于CDQ分治的伪代码示例,用于处理二维偏序问题: ```python def cdq_divide(l, r): if l == r: return mid = (l + r) // 2 cdq_divide(l, mid) cdq_divide(mid + 1, r) # 处理左区间对右区间的影响 # 例如:统计左区间中满足条件的元素对右区间的影响 # ... # 合并两个子区间并排序 # ... ``` 在具体实现中,通常需要结合归并排序的思想,以确保数据在分治过程中保持有序,从而提高效率[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值