P9531 [JOISC 2022 Day4] 复兴计划
首先有结论:对于每条边,在最终方案中会被选择的 X 是一个区间。
将所有的边按照边权排序,那么他被选择的代价关于 X 是一个绝对值函数。对于每条边,找出前一条能与他形成环的边,那么分界的 X 就是两条边边权的平均值。
找出每条边的 pre 可以在 O(nmα(n)) 的复杂度内完成,并将询问离线,按照 X 排序后维护一堆一次函数的和即可。
CF1801E Gasoline prices
对于每一个形如 {(u1,v1),(u2,v2)} 的限制,等价于将两条路径上对应点并查集合并,最后在同一个并查集中的元素的值必须相等,因此在求出最后并查集的合并情况后,只需要求出每个并查集连通块内部 L 的 max 与 R 的 min 即可,这一部分是容易计算的。
接下来考虑如何在合理的复杂度内计算出并查集的合并情况。
首先发现,将所有点全部合并联通只需要进行 n−1 次合并操作,因此有效的合并操作是 O(n) 的,只需要依次找到有效的合并操作即可。
考虑将每个点赋值上他所在并查集的祖先的点权,每次产生一个新的限制时,可以二分两条链上权值组成的序列的 LCP,第一个不同的位置即为需要合并的两个点。
因此只需要支持对于一个树上的单点修改,链上 hash 查询操作,可以直接把树拍成 dfn 序之后用树状数组维护。
时间复杂度 O(mlog2n+nα(n)+nlogV)。
P8162 [JOI 2022 Final] 让我们赢得选举
以下称收获一张票为 A,收获一个支持者为 B。
首先考虑在确定了最终哪几个选择 A,哪几个选择 B 时,一定是先按照 bi 从小到大依次完成 B,随后以任意顺序完成 A,所需要的时间为 ∑ibi+∣B∣+1∑ai。没有很好的贪心选择策略,考虑 dp。
由于 ∣B∣+1∑ai 的存在,因此在不确定 ∣B∣ 的前提下,很难对 A 进行 dp,因此首先枚举最终 B 选择了几个。
先按照 bi 排序,暴力 dp 为 fi,j,k 表示对于前 i 个人,当前 B 选了 j 个,A 选了 k 个,最小的时间是多少,但是这样时间复杂度为 O(n4),可能可以通过 wqs 二分优化到 O(n3logn)(我没证过),无法通过。
观察一下性质,在对 bi 排完序后,如果 i 选择了 B,那么 <i 的位置不可能不选,因为如果位置 j 不选,那么可以选择 j 成为 B 而不选 i,这样不会使结果更劣。
因此可以枚举最后一个选 B 的位置是 i,那么此时前面已经收获了 i 张票,后面需要的票数是确定的,直接贪心选择前 k−i 小的 aj 即可。
时间复杂度被优化至 O(n3),可以通过。
ABC219Ex Candles
首先有很显然的性质,由于吹灭蜡烛不需要时间,因此走到一个蜡烛的位置时一定会吹灭这根蜡烛,因此在对 a 排序后,吹灭的蜡烛一定是一段区间。而没走一步,所有还没有被吹灭的蜡烛的长度都会 −1。
但是如果每走一步都将不在区间内的蜡烛长度 −1 的话,会出现一些蜡烛的长度被减至负数的情况,导致计算得到的结果更小。
因此考虑对于 dp 状态加一维,表示仍未熄灭的且最终长度 ≥0 的蜡烛个数,显然这样不会得到比答案更优的结果。
因此令 fl,r,0/1,i 表示在吹灭的 [l,r] 区间内的蜡烛后,当前在 0/1(左/右 端点),还未被吹灭的会记入最终答案的蜡烛个数为 i 的最大答案。转移时,枚举这个蜡烛是否被计入最终答案即可。
时间复杂度 O(n3)。
P3488 [POI2009] LYZ-Ice Skates
不考虑修改,对于一次询问,建立二分图,左边的每个点表示一个人,右边的每个点表示一双鞋,如果存在一个对于左部点的完美匹配,那么答案为 YES。
根据 Hall 定理,对于左部点的任意一个集合,其对应的右部点的集合大小都应该要大于左部点集合大小。
考虑这张二分图的性质,左部的每个点都对应右部的连续 k(d+1) 个点,因此对于左部点,区间的条件不弱于集合的条件,因此只要对于左部点的每个区间合法即可。
考虑左部点 [l,r] 的区间,需要满足 ∑i=lrai≤k×(r−l+1+d),将每个 bi=ai−k,只需要满足 ∑i=lrbi≤k×d 即可,因此只需要拿线段树维护 b 的最大子段和即可。
CF1693C Keshi in Search of AmShZ
不难,但是很有借鉴价值的好题。
考虑题目要求求出最坏情况下的最少时间,因此每次走出边时,考虑走到仍未被删除的最坏的出边。
令 du 表示从 u 到终点所需要的最少天数。对于一点 u,如果没有割掉出边集合 S,那么 du=max(u,v)∈Sdv+degu−∣S∣。因此最终保留的边的集合一定是按照 dv 从小到大排序之后的一段前缀。
但是这张图并不是一张 DAG,所以并不能直接求解。考虑在进行 dijkstra 时有性质:当前枚举到的 dis 一定比之前枚举到的 dis 大,因此在进行一次 v→u 的更新操作时,选择 dv 作为排序后最后一个选择的没有割边的点,其最短路是 disv+degu−cntu,其中 cntu 表示已经更新过的 u 的出边数量。
直接跑 dijkstra 即可,时间复杂度 O(mlogn),本题巧妙之处在于利用了 dijkstra 过程中的性质来拟合了题目要求的条件。
P6623 [省选联考 2020 A 卷] 树
考虑每一位分别计算。每个点 u 对他的 k 级祖先会产生 au+k 的贡献,考虑将贡献差分,在每一位的 0/1 分界处将差分数组 ⊕1,但是这样总的差分数组更改量还是 O(n) 的。
考虑除了第一段,接下来二进制第 i 位的每一个 0/1 连续段的长度都是 2i,因此所有要更改差分的点的深度 mod2i 都是相等的,因此直接对于每一个二进制位开一个差分数组,ci,s 表示二进制后 i 位为 s 的点的差分应该要异或多少。
直接在 dfs 的时候维护差分数组即可,复杂度 O(nlogV),只需要枚举每个二进制位。
另一种解法是直接维护 01trie,需要支持插入元素,合并 01trie,每个数 +1 与全局求异或和,可以将 01trie 从低位往高位建,这样全局 +1 操作可以通过交换子树来完成,复杂度还是 O(nlogV)。
CF1152F2 Neko Rules the Catniverse (Large Version)
由于限制了每个数只能出现一次,如果按照序列的位置 dp,需要记录每个数是否出现,直接爆炸,因此考虑改变 dp 顺序。
考虑从小到大考虑每一个数 i,讨论他是否加入序列。
此时如果再记录每个位置是否填了数,那么状态数会飙升至 2k 往上,再乘上 n 又炸飞了。但是考虑到每个数的具体位置在 dp 过程中并不重要,我们只需要关心每个已经插入的数的相对位置即可,因此可以据此设计 dp 状态。
设 fi,j,k 表示已经考虑插入了 1∼i 这些数,其中加入序列中的数有 j 个,最后的四种数是否在序列中出现的状态集合为 k 的方案数。
由于题目要求对于相邻两个数,后面的数不能大于前面的数 +m,因此对于一个数 i,要么放在序列的开头,要么插在前 m 个数之后,要么不放,因此转移如下:
{fi,j,k→fi+1,j,k×2fi,j,k×(popcount(k)+1)→fi+1,j+1,k×2+1
对于 {j,k} 一共只有 k×2m 种,因此可以直接矩阵快速幂优化,时间复杂度 O(k38mlogn)。
P8688 [蓝桥杯 2019 省 A] 组合数问题
组合数转化成了数位 dp,卢卡斯和进制之间的转化,很新奇。
根据卢卡斯定理,有 (mn)≡(m/kn/k)×(mmodknmodk)(modk),发现这个东西就是将 n,m 写成 k 进制后对应位组合数的乘积。
题目要求 (yx)≡0(modk),由于 k 为质数,因此只要 x,y 在 k 进制下任意一位的组合数 modk=0 即可。又由于 x,y 在 k 进制下的每一位都 <k,因此其组合数 =0 只有可能是这一位上的 (a,b) 满足 a<b。
因此可以对 n,m 为上界,在 k 进制下数位 dp。令 fi,0/1,0/1,0/1 表示考虑了 k 进制的前 i 位,n 是否顶到上界,m 是否顶到上界,是否存在某一位 (a,b) 满足 a<b 的方案数。
转移可以直接分类讨论并计算方案数,时间复杂度 O(Tlogkn)。
CF1418F Equal Product
考虑对于满足条件的 (x1,y1,x2,y2),必有 x1⋅y1=x2⋅y2,因此 x2x1=y1y2=ba,因此 x2=ax1⋅b,y2=by1⋅a,并且可以证明 a∣x1 且 b∣y1。
通过这个转化,可以发现满足 a∣x1 的 (x1,a) 只有 O(nlogn) 对,可以直接枚举。
对于一个 x1,我们需要找出一对 (y1,b) 满足如下条件:
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧⌈x1L⌉≤y1≤⌊x1R⌋ax1⋅b≤ny1≤ma<b
发现在 x1 递增时,⌈x1L⌉ 与 ⌊x1R⌋ 都是单调不增的,因此可以时刻用一个 set 维护满足第一个条件的 (y1,b) 数对,每次查询时找到最小的满足 b>a 的数对 (y1,b),判断其是否满足第二个条件即可。时间复杂度 O(nlog2n)。
CF1774G Segment Covering
考察对于一对相互包含的区间 [li,ri]⊂[lj,rj],如果选择了区间 [lj,rj],那么多选择一个 [li,ri] 也无妨,因此在选择 [lj,rj] 的情况下,奇数方案与偶数方案个数相等,可以直接将 [lj,rj] 忽略。
现在问题被转化为了一堆左右端点都递增的线段,求恰好覆盖 [L,R] 的方案数。考虑一次询问怎么做。
设 fi 表示选择了 [li,ri] 线段,且恰好覆盖了 [L,ri] 这段区间的选偶数个线段的方案 − 选奇数个线段的方案。
初始状态有 f1=−1,转移为 fi=−∑rj≥lifj。模拟几次转移观察有什么性质。
对于第二条线段 [l2,r2],如果 l2>r1,那么方案数就变为了 0,因为始终无法覆盖中间一段区间,否则 f2=1。
对于第三条线段 [l3,r3],如果 l1≤l2≤l3≤r1,那么有 f3=f2+f1=0,可以看作 f3 不存在。
同理地,对于接下来的连续一段满足该条件的线段,都有 fi=0。
直到第一条满足 li>r1 的线段 i,如果 li>r2,那么 fi=0,后面的 f 也全部为 0,否则 fi=−f2=−1。
这条线段的下一条线段 i+1 也有 1→2 类似的转移方式。由此解决了一组询问的问题。
考虑 pi 表示第一条满足 lj>ri 的线段 j,那么我们维护这两个 f 值为 −1 与 1 的线段 x 与 y,其实是一直在做 x=px,y=py 操作,如果存在两条线段不交那么就无解。
此时观察到一个很妙的性质,如果中途出现了两条线段不交,那么下一步中后面的线段会走到前面的线段位置上,从而使得这两条线段最终重合。而如果不存在线段不交的情况,那么最后两条线段不会重合。
因此将 i→pi 连边,构成一个内向树的结构,每次询问时倍增跳父亲,答案可以根据最后 x,y 的位置计算得出。
CF1305G Kuroni and Antihype
首先考虑加入一个虚点,点权为 0,初始时 0 在组织中,这样就能保证最终一定存在一种最优状态是所有人都在组织中的。
假设将一个人加入组织时,被邀请的人也能获得收益,那么一对人相互邀请的贡献是确定了,最后只需要减去所有人的收益和(加入组织实际上是没有收益的)即可。
考虑在能相互邀请的人之间连边,边权为 au+av。原问题等价于求出这棵树的最大生成树。
虽然这棵树的边树可能会很多,但是把相同点权的点缩起来之后,本质不同的边只有 318 条。可以从大到小枚举边权,然后枚举两边点,用 kruskal 求解最大生成树即可。时间复杂度 O(n+318α(318))。
CF1019C Sergey's problem
首先考虑忽略第一个限制,找出一个点集使得从这个点集出发走一步能走到所有点。
对于每个点维护一个标记,表示这个点是否能被当前点集中的至少一个点走一步后到达。依次枚举每个点,如果这个点未被标记,那么将这个点加入点集,并将这个点的所有出边能到达的点进行标记。
不难发现这样构造出的点集能在之多一条边内走到所有点,并且这个点集的导出子图是一个 DAG!
证明考虑记录每一个点被标记的时间戳 tu,对于一条边 u→v,如果 u,v 都在点集中,那么一定有 tv<tu,否则在标记 u 时一定会将 v 标记。因此对于导出子图中的每一条边 u→v,必有 tu>tv。因此这张导出子图一定是 DAG。
考虑对 DAG 拓扑排序,也类似第一次构造的形式进行标记,那么最终选择的点集内的点至少一步能走到第一次选出的点集,从而保证了至少两步能够到达所有点,也满足的点集内的点两两之间不变的限制。
CF1553I Stairs
不难发现所有极长的连续自然数按顺序组成的区间互不相交,因此可以根据 a 数组推断出连续段依次的长度,假设为 b,长度为 m。
现在的限制是每个连续段内的值连续且递增或递减,且任意相邻的两个连续段的值域都不能相邻(不然就能拼接起来了,不符合条件)。
考虑容斥,假设钦定 i 对相邻的区间之间的值域是相邻(不合法)的,那么区间被合并为了 m−i 段,给每一段分配数的方案数是 (m−i)!,每一个长度 ≥2 的段能选择递增还是递减。
考虑列出 dp 转移方程,设 fi,j 表示考虑了前 i 段,现在合并出了 j 个连续段的方案数。有:
{fi,j=2×∑k<ifk,j−1fi,j=fi−1,j−1+2×∑k<i−1fk,j−1bi=1bi=1
此时,设 fi,… 的生成函数为 Fi,f 的前缀和的生成函数为 Gi,那么有转移:
⎩⎪⎪⎪⎨⎪⎪⎪⎧[Gi−1Fi]=[Gi−2Fi−1]×[112x2x][Gi−1Fi]=[Gi−2Fi−1]×[112xx]bi=1bi=1
可以直接通过多项式分治乘法求解出最后的 dp 矩阵,再根据最后的 dp 值容易算出答案。复杂度 O(nlog2n)。
P8990 [北大集训 2021] 小明的树
有点诈骗的感觉,但是总体还是不错的一道题。以下称点亮的点为白点,未点亮的点为黑点。
题目的合法性要求是所有的白点的子树内都是白点,因此原树中一定存在一些互不相交的全部为白点的子树。而对于所有的黑点,一定都存在于与 1 联通的连通块内。至此,合法条件被转化为了所有的黑点都在一个连通块内。
由于所有黑点的连通块构成的依旧是树的形态,因此对于森林的连通块个数,我们可以通过点数 − 边数计算,也就是在第 i 次操作和,合法的条件等价于 n−i− 连结两个黑色节点的边数 =1。
再考虑所有合法状态下对答案的贡献,是所有白点的连通块个数,也就是所有连结黑点与白点的边的个数。
至此,树的形态已经不重要了,我们只关心每个操作后每种边的个数。对于一个询问,我们需要求出所有 n−i− 连结两个黑色节点的边数 =1 的时刻的连结两个黑色节点的边数。
由于 n−i− 连结两个黑色节点的边数 ≥1,所以我们可以直接将两个东西看成一个二元组 (a,b),询问需要求出 a 最小的二元组的 b 之和,可以直接放到线段树上维护,修改时就是区间二元组每个元素的 +/− 操作,容易维护。