杂题小记(2023.03.21)

更好的阅读体验戳此进入

实际上是 2023.03.17-2023.03.21

LG-P3233 [HNOI2014]世界树

细节怪。

需要建虚树很套路,然后考虑维护虚树内和虚树外的,对于虚树内的是平凡的,对于虚树外难点在于考虑一条链的分割,不难想到通过倍增实现即可,思路比较清晰不是很难但是码量巨大。

LG-P5360 [SDOI2019]世界地图

简单提一下一些细节问题。

首先考虑一下本题的大致思路,构建前缀 MST 和后缀 MST 每次询问合成即可。合成时通过将两个 MST 所有边合成为新的 MST 然后通过虚树的思想只保留关键点,将树简化以保证点数级别。

  1. 点的重标号

这是我第一个卡住的点,最开始的思路就是朴素地开一堆 unordered_map 然后手写 pair < int, int > 的哈希建出来一堆映射,将坐标映射为点,然后每次建 MST 或者虚树的时候都重搞一遍,写了一会发现这东西常数似乎有些爆炸,且细节巨多,翻了一下题解区理解了一会才明白这个重标号点的思路:

我们还是回到合成 MST 的过程中,发现如对于 [1,i1][1,i] 的过程,关键点的纵坐标从 1i1 变为 1$ i $,连结的所有边为 i1 i,这样我们就容易理解大多数题解的映射方式了,使所有 MST 满足前 n 个是第 1 列,后 n 个是最后 1 列,这个过程在一般的实现中是自然的。合成 AB 两个 MST 的时候,直接按序将 B 的点接到 A 之后,然后对 A 的后 n 个分别与 B 的前 n 个连结,并将 A 的前 nB 的后 n 作为关键点,这样即可优美地解决点的重标号。

  1. 关键点的选取

对于上述点的重标号的过程,对于 1 列的钦定选择的原因,个人理解就是对于维护前后缀的时候,钦定 1 列为关键点显然是非必要的,但维护后就可以同时适配合并前后缀,应该属于是写法的优化。

  1. 虚树的构建

做这道题的时候本来是准备直接写两次按 dfn 排序的朴素建虚树的,后来看到题解区用的都是两次 dfs,不难发现这样是可以减少不少常数的,因为每次的树的形态都完全不同,每次都需要重构树剖或者倍增,于是就不如 O(n) 的两次 dfs 了。

  1. 前后缀的合并

这里注意需要用后缀在前前缀在后进行合成,因为按照我的写法合并是有序的,即是对 A 的后 nB 的前 n,如果前缀在前后缀在后连边就会反了。

  1. 资源的回收

不难发现对于 500MiB 的限制按照我的写法最多开 2×107 条边,这是不够用的,所以需要实现对边的复用,对应着代码中的:

改为:

注意这里无需清空边的内存池是因为每次我调用 new 的时候都对其进行了初始化列表的初始化。

代码大同小异。

LG-P4197 Peaks

考虑建出 Kruskal 重构树,此时对于每次查询可以在线转换为倍增找到最优祖先之后整个子树中对叶子节点的静态区间第 k 大,用主席树维护即可,细节略多。

LG-P4768 [NOI2018] 归程

考虑建出 MST,不过是最大生成树,然后倍增一下,完全类比上一题去做就行,需要 Dijkstra 跑一下最短路,然后挂到线段树上求区间最小值,亦可直接维护。

LG-P4899 [IOI2018] werewolf 狼人

好题,难度不大,实现直观。

考虑点权转边权,分别构建最小生成树和最大生成树的 Kruskal 生成树(最大生成树用点权最小值作为边权,反之亦然),老样子维护倍增找到最大可行区间,问题转化为对两个排列的两个区间求是否有交。

考虑维护每个元素在每个排列中的位置,问题转化为二位数点,第一维作为主席树下标,第二维作为权值跑一下即可。

LG-P1446 [HNOI2008] Cards

直接套用 Burnside,发现问题转化为求对于每个置换以及单位元的不动点数量,对于每个置换的求解过程考虑将其形成的所有环的长度梳理出来然后对三个颜色做一下背包即可,具体地,考虑 dp(c,i,j,k) 表示前 c 个环三种颜色分别有 i,j,k 个的方案数,按照背包思想滚动一维倒序转移即可。

UVA11255 Necklace

 

 

 

 

UPD

update-2023__ 初稿