#CDQ分治#洛谷 3810 bzoj 3262 陌上花开

本文深入解析CDQ分治算法结合树状数组的应用,通过具体代码实例,详细阐述了如何实时维护和更新数据结构,实现高效的数据查询与处理。特别关注于分治策略下的树状数组恢复过程,为复杂问题提供了一种有效的解决方案。

题目


分析

CDQ板子,用树状数组实时维护,分治完后要恢复树状数组


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int N=100101;
int n,m,ans[N],k,a[N],b[N],c[N],rk[N],qk[N],w[N],cnt[N],h[N<<1];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline bool cmp(int x,int y){return a[x]<a[y]||(a[x]==a[y]&&(b[x]<b[y]||(b[x]==b[y]&&c[x]<c[y])));}
inline void update(int x,int y){for (;x<=k;x+=-x&x) h[x]+=y;}
inline signed answ(int x){rr int ans=0; for (;x;x-=-x&x) ans+=h[x]; return ans;}
inline void cdq(int *rk,int n){
	if (n==1) return;
	rr int mid=n>>1,i,j,k;
	cdq(rk,mid),cdq(rk+mid,n-mid);
	memcpy(qk,rk,n<<2);
	for (k=i=0,j=mid;i<mid&&j<n;++k){
		rr int x=qk[i],y=qk[j];
		if (b[x]<=b[y]) update(c[rk[k]=x],w[x]),++i;
		    else cnt[y]+=answ(c[rk[k]=y]),++j;
	}
	for (;j<n;++j) cnt[qk[j]]+=answ(c[qk[j]]);
	memcpy(rk+k,qk+i,(mid-i)<<2);
	for (--i;~i;--i) update(c[qk[i]],-w[qk[i]]);
}
signed main(){
	m=iut(); k=iut();
	for (rr int i=0;i<m;++i) rk[i]=i,a[i]=iut(),b[i]=iut(),c[i]=iut();
	sort(rk,rk+m,cmp);
	for (rr int i=1;i<m;++i){
		rr int x=rk[i],y=rk[n]; ++w[y];
		if ((a[x]^a[y])||(b[x]^b[y])||(c[x]^c[y])) rk[++n]=x;
	}
	++w[rk[n]],++n; cdq(rk,n);
	for (rr int i=0;i<n;++i) ans[cnt[rk[i]]+w[rk[i]]-1]+=w[rk[i]];
	for (rr int i=0;i<m;++i) print(ans[i]),putchar(10);
	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]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值