不如来搞一下CDQ分治吧!

本文深入探讨了CDQ分治算法,一种高效处理大规模数据集的技术。通过将问题分解为两个独立子集,并分别求解,最后合并结果,CDQ分治在处理如三维偏序问题等复杂场景时展现出强大能力。文章还提供了CF669E、CF762E、CF848C等竞赛题目的具体应用案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CDQ分治

CDQ分治是一种时间魔术。

在正常的世界中,韶华易逝,时间总是在向前流逝着。只有过去和现在能决定未来,未来无法反过来决定现在和过去,不然就会发生世界线错乱的现象。

CDQ分治同样顺应着时间向前流逝这条规律。

总共有n秒,每秒钟都发生了一起事件。

CDQ分治的操作如下。

  • 现在\(n/2\)秒处,建立起一道时间壁垒。将这个世界分为两个结界。第一个结界的时间是\([1,n/2]\),第二个结界的时间是\([n/2+1,n]\)
  • 注意到第二个结界无法影响第一个结界,但第一个结界能影响第二个结界。所以我们可以结算第一个结界对第二个结界的影响。
  • 影响结算完后,这两个世界之间所有关联皆被斩断。接下来对这两个结界分而治之即可。

看看著名的三维偏序问题。

我们对第一维cdq分治,对第二维进行排序,对第3维进行BIT线段树维护。
其实,就是cdq分治套一个二维偏序关系问题!

看几个栗子

CF669E:Little Artem and Time Machine

题意:一个multiset,需要支持以下操作,1.到\(t\)时刻,插入\(x\) 2.在\(t\)时刻删除\(x\) 3.查询\(t\)时刻\(x\)的个数.

做法

  • 我们按照\(t\)对操作进行排序,然后把\(t\)当成时间进行CDQ分治即可。

code:

CF762E:Radio stations

题意:在坐标轴上有很多个广播站,第i个,位置为x[i],覆盖半径为r[i],频率为f[i],如果两个站\(i,j(i<j)\),都在对方半径内,且频率差的绝对值不超过k,那么就把这样的\((i,j)\)成为bad,问有多少个bad的pair.

做法

  • \(r\)从到大到小排序,把\(r\)当成时间。如此一来,我们只要时间靠后的,能覆盖住时间靠前的即可。
  • 有多少个\(i,j\),满足\(x_j-r_i\leq x_i\leq x_j+r_j\),$ r_i-k\leq r_i \leq r_i+k$即可。问题转化为经典的三维偏序问题。我们可以拆成4个矩形,容斥一下就好。

code:

看了考虑清楚按哪一维CDQ分治,能够使得算答案比较方便,很重要啊

CF848C:Goodbye Souvenir

题意:给序列a,有一些。操作为为两种,操作1:a[p]=x,操作2:区间查询[l,r],查询此区间内,每个元素,最后一次出现的位置 - 第一次出线的位置。

做法

  • 先考虑不带修改,怎么做。对于每个元素a[i],我们记录它上一次出现的位置pre[i], 对于区间\([l,r]\)\(\sum_{[pre_i \geq l, i \leq r]} (i-pre[i])\)就是答案了。
  • 问题可以转化为\((操作顺序, i, pre_i)\)的一个三维偏序问题。
  • 我们对于操作的次序这一维进行cdq分治。计算\([l,mid]\)\([mid+1,r]\)答案的贡献的时候,我们对\(i\)排序,把\(pre[i]\)插入树状数组即可。

code:

哇,我的这种写法看来有点慢啊。
目前,我的写法是,先算\([l,mid]\)\([mid+1,r]\)的贡献,再递归地处理他们。但先递归搞\([l,mid]\),\([mid+1,r]\)。递归的时候顺便完成对第二维的归并排序。再算\([l,mid]\)\([mid+1,r]\)的贡献似乎更好一点。

转载于:https://www.cnblogs.com/RUSH-D-CAT/p/9749185.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值