【总结】cdq分治

Part 1.

拦截导弹

首先考虑用 cdq分治 求解 LIS。 i i i j j j 的贡献可以看做 d p j = m a x ( d p j , d p i + 1 ) dp_j=max(dp_j,dp_i+1) dpj=max(dpj,dpi+1) 。这样的话可以考虑先求解 c d q ( 1 , m i d ) cdq(1,mid) cdq(1,mid) ,再计算跨区间的贡献,最后计算 c d q ( m i d + 1 , r ) cdq(mid+1,r) cdq(mid+1,r) 。归并排序维护数值的有序性,注意到左半区间是有序的,而右半区间是无序的,如果将右半区间排序的话,就分不清到底是左边贡献右边还是右边贡献左边了。所以要对左右区间分别排序。准确地说,只需要临时将右区间排序。

然后本题的式子是 f j = m a x ( f j , f i + 1 ) f_j=max(f_j,f_i+1) fj=max(fj,fi+1) g j = ∑ g i ( f j = f i + 1 ) g_j=\sum{g_i}(f_j=f_i+1) gj=gi(fj=fi+1)

可以正着倒着跑一遍,这样每一个点的方案数为 g i g 2 i g_ig2_i gig2i ,总方案数为 ∑ g i ( f i = t o t ) \sum{g_i}(f_i=tot) gi(fi=tot) ,注意这里不能是 ∑ g i g 2 i ( f i + f 2 i − 1 = t o t ) \sum{g_ig2_i}(f_i+f2_i-1=tot) gig2i(fi+f2i1=tot) ,因为计算的是单个点出现时的方案数,显然会算重。

然后左右区间排序时要用 t m p tmp tmp 临时数组,存一下下标,再还原回去。因为此时 c d q ( m i d + 1 , r ) cdq(mid+1,r) cdq(mid+1,r) 还没有处理,会打乱第一维的顺序。

当然下面这道题是可以直接将 q u e r y query query 数组排序的,因为左右子区间已经处理完了,可以合并成新的无序区间。

动态逆序对

话说这道题用二维树状数组不是很显然吗。时间复杂度 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

然后 c d q cdq cdq 分治会保证 a a a 单调递增,内部用一维树状数组维护,再加上时间戳的限制,大概也差不多。

总的来说,就是把序列删掉后剩下的数当成修改操作加进去,每次操作就拆分成一次查询和一次修改。

然后这里注意一个事实,就是修改操作必须先计算出增加它后的贡献,再算给查询操作,所以计算修改还是单点对单点,贡献为1,只不过多加了计算全局贡献的查询操作,用 s u m sum sum 维护一下就行了。

注意到一个细节问题,那就是查询操作没有实际意义,无法比较大小,那么每次归并时应该放在最前面。

然后发现一个问题,就是这里元素的贡献是有序对,也就是说第一维必须严格按照 a a a 的递减排序,这里时间戳的顺序是不成立的。

那么问题来了,每次修改的元素是无序的呀,可能前面的数更新它,总不能重新给它插入一个合适的位置吧。

然后我们发现如果计算两次逆序对,那么恰好会被计算一次。

然后你发现查询操作查询操作比较麻烦,因为修改操作的贡献在改变,你不知道什么时候累加了修改操作的贡献。

所以把前一部分做出来就行了,后一部分直接 c d q cdq cdq 分治后算一个前缀和即可。

然后就是本题给的是个排列,删除的是元素值

总结

c d q cdq cdq 分治没有固定的写法,要看应用在怎样的背景中,多动动脑就想明白了。


Part 2.

[NOI2007] 货币兑换

A k y + B k x = I P A_ky+B_kx=IP Aky+Bkx=IP

y = R a t e x y=Rate_x y=Ratex

x = I P A k R a t e k + B k , y = I P ∗ R a t e k A K R a t e k + B k x=\frac{IP}{A_kRate_k+B_k},y=\frac{IP*Rate_k}{A_KRate_k+B_k} x=AkRatek+BkIP,y=AKRatek+BkIPRatek

d p i = d p j + y A i + x B i = d p j + B i ( x + y A i B i ) dp_i=dp_j+yA_i+xB_i=dp_j+B_i(x+y\frac{A_i}{B_i}) dpi=dpj+yAi+xBi=dpj+Bi(x+yBiAi)

这不就是一个斜率优化嘛。考虑用李超线段树怎么做,我们知道李超树用于查询 x = k x=k x=k 时的线段上的最值。右边式子恰好是 y = a x + b y=ax+b y=ax+b 的形式,取 x = A i B i x=\frac{A_i}{B_i} x=BiAi 即可。

城市建设

必须辅助数据结构维护一些东西。

我们把这张图划分成两个部分,动态边静态边

如果 c d q cdq cdq 考虑 ( l , r ) (l,r) (l,r) 这一段操作序列,那么 l l l, r r r 内操作涉及的边就称为动态边,这张图其余的边被称为静态边。

很冒险,但是很宏大。

我们考虑简化静态边集合。将不会出现的边从静态边集中删去,将一定会出现的边在静态边集中缩点。(话说有毛用?一头雾水。)

  1. 如果把区间内的边全部修改为 -inf ,做mst,树上的其他边就是必须选的。(换句话说,动态边就只有r-l个,你静态边缩点出来的点的个数必须比它小,你静态边连出来的联通块可以看成一个点)
  2. 如果把区间内的边全部修改为 inf,做mst,非树上的其他边就是必定不选的。(换句话说,能用的静态边最多也只有r-l个)

这一步保证了 ∣ V ∣ , ∣ E ∣ = O ( r − l ) |V|,|E|=O(r-l) V,E=O(rl)

我们发现,会有一些动态边变为静态边,最后变成了一个纯静态的 k r u s k a l kruskal kruskal

边界条件:l==r或n=1。

注意, c d q cdq cdq 并没有脱离整体而存在,实际上它的研究整体依然是整个边集,这一点可以从暴力传参看出。

比较恶心的一点是缩点过后的序号和边并不对应,所以事实上我们要将并查集保留下来。然后最tm坑的是因为我们在子区间中都是操作的并查集的根,所以事先要把 所有边 的两个端点和根连起来,但不是真正赋值(我说了只能是利用并查集,不能改变自身)。然后tm并查集是可撤销的,在做完左区间回来时,只用把代表的爸爸变回自己即可

第三遍重构了。。。

我tm不知道这个题为什么不能打路径压缩,哦对了,所有带撤销的并查集都不能这么干(如线段树分治),因为会造成父子信息的丢失。注意 带撤销的并查集不等于可持久化并查集

总结

c d q cdq cdq 分治的思想博大精深,可以是将动态问题转化成静态问题,也可以是基于分治时间戳的玄学算法,总之目的在于将每次问题求解的规模控制在 O ( r − l ) O(r-l) O(rl) ,这样很多不合理的暴力算法就变得可能了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值