【学习笔记】杂项

对于本版块:会把不好放进其他模块的独立算法放进来。

cdq 分治

P3810 陌上花开

我们考虑二维偏序是怎么处理的。显然我们对第一维排序然后对第二维开树状数组统计。

同样我们尝试把第一维排序,然后尝试进行分治。将当前分治段分成左右两部分,并把这两部分分别处理好。

inline bool cmp1(Node c1,Node c2){
return ((c1.x^c2.x)?c1.x<c2.x:((c1.y^c2.y)?c1.y<c2.y:c1.z<c2.z));
}//第一次排序

我们显然能够发现在分治的小区间处理好后,在当前范围内左区间不会被更改,但是这种结论在有三元组完全相等时无效,所以我们把三元组去重。

然后我们发现左右区间都处理完答案后第一维不重要了,因为我们只用知道 在现在这个区间只剩左区间对右区间的贡献没有被统计了

然后是不是变成了二维数点。我们类似归并的做法把左右端点排个序。把左右区间按第二维排个序。

inline bool cmp2(Node c1,Node c2){
return ((c1.y^c2.y)?c1.y<c2.y:c1.z<c2.z);
}//第二次排序

按照第二维,对左区间加进一个树状数组中,对右区间查询第三维。

注意退出的时候清空树状数组。

考虑把分治的二叉树表示出来,每次都遍历每一整层,每一层长度都是 n,遍历一个长度为 l 的区间因为带排序和树状数组是 O(llogl) 的,所以遍历一层大约是 O(nlogn),一共是 logn 层,所以总复杂度是 O(nlog2n)

更多维的偏序:P5621 德丽莎世界第一可爱

四维偏序,考虑 cdq 套 cdq 优化 dp。

外面套 cdq 的那一层为了传到里面给每个点设一个 lf 代表这个点在外套的 cdq 里面是不是给贡献的点,然后就是普通 cdq 了。

因为是优化 dp,所以 cdq 的顺序是左中右,也就是先 cdq 左边,然后处理这一层的数据,然后 cdq 右边。

因为先处理这一层的数据然后再搞右边,为了使右边仍然有序,要新开一个数组搞。

题解,很多东西放这里面了。

整体二分

适用于有多次询问,每次询问都可以二分,二分过程都相似。此时我们可以考虑把这些询问一起处理。

具体地,定义函数 solve(al,ar,l,r) 表示现在答案值域在 [al,ar] 之间,答案在这个值域内的问题区间是 [l,r],那么我们可以发现这个东西要求问题答案连续,所以我们在函数内进行问题的归并。

每次进行函数的时候,因为是二分所以要搞一个 mid 来进行新的转移,然后检查 [l,mid] 可能是哪些问题的答案然后把这两个问题划分传到下一层。

普通二分的终点是答案值域只剩一个数,在这里体现为 al=ar,那么答案在这个值域的区间答案都是 al

复杂度 O(nlogn),乘上一个检查答案的复杂度。

挂一个 离线动态区间 k 小值,只要不是强制在线就可以放心写整体二分,复杂度和树套树一样是 log2 的。

反悔贪心

对于部分假贪心的题目,若所有选择带来的消耗一样,那么我们能够处理出来当前选择的东西里面价值最小的那一个,当没有空位选择下一个东西的时候,我们就能够把这个价值最小的物品和当前想要加进来的东西作比较然后扔掉一个。

因为涉及到加入和时刻查询最小价值,所以考虑堆来维护。

01 分数规划

每个物品有两个属性 a,b,让你选一些数得到 max/minab

首先这个东西具有单调性,这个很显然。所以可以考虑二分这个比值是否 mid

拆一下式子就是问能不能有 (amid×b)0。发现答案变得只跟每个单独物品有关,按照题目要求搞就行。

对于边界的判断,在第二属性为物品个数,即比值为平均值时边界取权值最大最小值。其他情况不知道。

康托展开

对于一个排列 pn ,求他在 n 的全排列中的排名。

ans=1+i=1nj=i+1n[aj<ai]×(ni)!。即我们按字典序看当前一位会产生多少个排名更小的排列。相当于在后面选填在这一位能够比当前排名大的数。

虚树

给你 k 个关键点 a1,a2,,ak,让你在原树上保留这些关键点以及它们的 lca 建出来虚树。目前主流有两种做法。

第一种做法是 按照 dfs 序排序k 个点然后求出 lca(a1,a2),lca(a2,a3),,lca(ak1,ak)

然后对这 2k1 个数去重并再次按照 dfs 序排序,连接 (lca(a1,a2),a2),(lca(a2,a3),a3),,(lca(ak1,ak),ak)

首先证明第二次求 lca 时求得的 lca 都已经在此时的 a 序列中出现过:

  • 按照是否是给出的关键点将 a 序列的点分成关键点和非关键点。令第二次求 lca 时求得 cx,y 的 lca。

  • x,y 有祖先后代关系那根本不用证了,lca(x,y) 就是深度更浅的那个点,一个点本身肯定在序列里。于是令 x,y 不是祖先后代关系。

  • x,y 均为关键点,根据 dfs 序的排序规则它们在第一次排序时也一定相邻,于是我们在第一次排序时已经将 c 加入了序列。

  • x 为关键点,y 非关键点(反之亦然),则 y 子树内一定还有至少多于一个关键点,否则 y 不可能被加入序列。令其子树中 dfs 序最小的点为 p。观察到按照 dfs 序排序后一个点的子树一定是直接接在在它后面的一个连续段,所以第一次排序后一定有 x,p 相邻,而 x 并不在 y 子树内,所以 lca(x,p) 一定是 y 的祖先,则 c 一定就是 lca(x,p)

  • x,y 均非关键点,注意到按照上一条证明,若 x,y 无祖先后代关系,此情况下则 x 的子树为空,但是 x 非关键点,于是 x 根本不可能被加入序列。得到此种情况不会发生。

好的那么注意到我们对新的序列(令长度为 m)中的 m 个点连了 m1 条边,同时由于两个点的 lca 的 dfs 序一定大于这两个点的 dfs 序,于是我们不可能会连出重边或者自环,于是至少我们得到的是一棵树型结构。

然后感性理解一下这个东西应该是能把所有 lca 搞出来的。

然后因为 dfs 序的性质我们连 (lca(x,y),y) 时,lca(x,y)y 这段一定没有 a 序列上的点,因为这一段的 dfs 序小于 y 的 dfs 序。所以说我们不会连错边。那么虚树的正确性应该就能证完了吧?

观察到求 m1 次 lca 最多增加 m1 个新节点,于是可以证明出来虚树的点数上界是 2m1。实际写的时候可以把原树的根节点先塞到虚树里面。

第二种做法是用 单调栈维护右链,感觉和笛卡尔树差不多的想法?

就是你维护最右边那条链,每次加入一个关键点的时候跟栈顶做一个 lca,如果不是栈顶就弹栈弹到栈顶变成那个 lca 或者是那个 lca 的祖先。在弹出的时候连边即可。

在原树节点数本身较少时可以 暴力建树,先统计含有关键点的不同子树个数标记虚树上节点,然后连接祖先后代关系的两个节点。不过基本用不上就是了。

posted @   Wind_Leaves_ShaDow  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示