杂题记录2

ABC083D Wide Flip

上下界分析法。

考虑两个相邻不同的位置,分界线为 \((i,i+1)\),那么显然要在左边或者右边操作一次。于是对于该位置,答案的上界为 \(\max(i,n-i)\)。最后总答案的上界是对于所有位置的上界取 \(\min\)。大胆猜测可以对于这个条件推至充要。

考虑构造一下,如果我们对于 \((i,i+1)\) 进行操作。那么除了这个分界点位置的所有不同的 \(01\) 位置,显然还是不同,也就是说 \(01\to 10,10\to 01\)

QOJ9541.Expanding Array

有一个序列,每次操作可以在相邻两个位置 \(a_i,a_{i+1}\) 之间插入他们的 \(xor,or,and\)。可以无限操作,求序列不同值的最大值。\(n\le 10^5\)

可以让电脑随机操作跑很久,看看能生成多少个数。最大是 \(8\) 个。

考虑对于每一位考虑两个数在这一位上的取值二元组,可以发现对于所有的 \(01\) 都是相同变化的,要么 \(0\),要么 \(1\)。对于 \(10,00\) 同理。所以相邻两个数的最大贡献就是 \(2^3=8\),暴力算出相邻两个数的所有贡献,数量是 \(O(n)\) 的。插入集合,查询个数即可。时间复杂度 \(O(n\log n)\)

CF1626E Black and White Tree

不同根的 \(O(n^2)\) 做法很好想。就是以某个点为根后,存在以下结构,某个黑点父亲的子树内存在至少两个黑点。

换根不太好写,考虑另一种刻画方式。如何刻画不同根?把根信息放到每条边的定向上,BFS 即可。

判定条件就是如果如果一条边某个端点子树内有两个黑点或者其中一个点为黑点就可以往这里走。

CF1183F Topforces Strikes Back

手算几种情况,会发现最大值基本都是要选择的。

假设最大值为 \(n\),那么我们不选 \(n\) 的时候的最优情况为 \(\dfrac{n}{2}+\dfrac{n}{3}+\dfrac{n}{5}\)(之所以第三个分母不是 \(4\),是因为防止和 \(\dfrac{n}{2}\) 冲突)。可以发现如果让 \(\dfrac{n}{5}\) 再小点的话就不如 \(n\) 了。所以要么就选 \(\max\),要么 \(\dfrac{n}{2}+\dfrac{n}{3}+\dfrac{n}{5}\)

P8029[COCI2021-2022#3] Akcija

很经典的前 \(k\) 优方案的问题。目前大概看过两种类型,一个是类似于局部选择可以排序一下,然后维护指针不断加入。另一个是像本题这样子类似全局选可以考虑 \(\operatorname{A*}\)。维护一个估计函数 \(f\) 代表后缀最优代价, 然后从 \(1\) 开始顺推。

放到图上形式化地描述就是前者是离源点最近的 \(k\) 个点,可以维护指针不断扩展。后者是 \(k\) 短路,需要我们用 \(\operatorname{A*}\) 来解决。

\(d_i\) 为时间约束,我们不妨从小到大排序。

本题的实现方法就是先求出 \(f_{i,j}\) 表示后面 \(n-i\) 个物品我们选择了 \(k-j\) 个的最小代价。也就是一个后缀最优函数。这个很容易通过 dp 来求解。

然后我们从 \(1\) 开始顺推,设当前局面状态为 \(s\) 前缀代价为 \(g_s\)。我们只需要维护前 \(k\) 大的 \(g_s+f_s\) 即可。因为 \(f_s\) 仅仅表示后缀最小代价,可能还会有次小等等之类,也就是说 \(s\) 后面可能会扩展出多个状态,其中一个最好的情况是 \(f_s+g_s\)。故当前局面的前 \(k\) 优可以推出最后局面的前 \(k\) 优。而当前局面的第 \(k+1\) 优的最佳情况已经不如前 \(k\) 个状态优了,所以必定不可能成为最后的前 \(k\) 优方案。

有一个技巧就是我们并不需要维护前 \(k\) 状态的相对大小,只需要找到前 \(k\) 小,所以直接用
nth_element 函数即可,而不用排序或者优先队列,可以省去一个 \(\log\) 的复杂度。

时间复杂度 \(O(n^2+nk)\)

P10785 [NOI2024] 集合

很牛的一道哈希题。

首先尝试刻画一个条件来判定合法,经过我们手玩+尝试/从集合大小为 \(1\) 开始思考并推广,之后可以发现条件是 \(a\)\(b\)\([l,r]\) 内每个数的出现位置的小集合组成的大集合相等。

我们不太方便用一个数据结构来一次查询 \([l,r]\) 的哈希值,于是考虑莫队。但是我们要设计出一个可以支持动态加减的元素的哈希,如果采用常规的 \(P\) 进制其实不太好刻画无序集合&动态删改元素之后的顺序性。需要用一个具有快速可逆性的运算,可以用 "和" 哈希。

\[H(l,r)=\sum\limits_{\exists i\in[l,r],x_\in a_i}G(F(set_{x})) \]

对于 \(F\) 是一个集合哈希,可以对于每个位置下标都赋一个随机数,然后 \(F\) 就是把集合内的的数异或起来,\(G\) 是再把这个数变化一下,一般常用多项式结合位运算随便搞一个就行了。这样是时间复杂度是 \(O(n\sqrt q)\) 的。

使用双指针即可做到线性。哈希部分可以多做几次提高正确性。

P5426 [USACO19OPEN] Balancing Inversions G

首先交换相邻不同 \(01\) 之后逆序对变化是 \(1\)。设左右逆序对个数为 \(n_1,n_2\),所以有一种方案是对于左边或者右边内部调整,次数是 \(\lvert n_1-n_2\rvert\)

发现如果我们跨过中点移动,变化量可能会很大,使得答案更优。

这里分为 \(01\) 交换和 \(10\) 交换,不难发现只有可能选其中的一种来调整(一次 \(01\),一次 \(10\) 就复原了),下面以 \(01\) 交换为例。

我们暴力枚举交换次数,也就是说不断把左边的 \(0\) 和右边的 \(1\) 拉过去交换,每次交换之后用当前的移动次数 \(+\lvert n_1-n_2\rvert\) 更新答案。注意每次选择是最靠近中轴线的 \(0\)\(1\) 出来交换。

时间复杂度 \(O(n)\)

AGC003E Sequential operations on Sequence

首先需要排除无用的 \(a_i\),直接单调栈处理出来一个递增的序列即可。

由于不是动态询问,而是最后再统计,所以我们应该思考一下贡献法之类的而不是数据结构维护。

\(b_i\) 为第 \(i\) 次操作之后的序列,那么 \(b_i\) 就是由 \(b_{i-1}\) 复制了 \(\lfloor\dfrac{a_i}{a_{i-1}}\rfloor\) 次之后,加上了零散块前 \(a_i\bmod a_{i-1}\) 个位置。对于第一类贡献好处理,但是第二类有点难办,这里的方法特别巧妙,直接把贡献放到前面去。对于 \(a_i\bmod a_{i-1}\) 的部分其实在前面就已经成型了,也就是在 \(a_j<a_i\bmod a_{i-1}\) 的时候就已经确定了前 \(a_j\) 个,还需要再继续在 \(j+1\) 处算 \((a_i\bmod a_{i-1}) \bmod a_j\) 的贡献,继续递归计算即可。贡献可以用差分标记一下。

发现每次取模数量大小至少变成之前的 \(\dfrac{1}{2}\),所以单次递归次数是 \(\log\) 级别的,配合上二分就是 $2\log $,因此时间复杂度是 \(O(n\log^2n)\)

CF1919F1 Wine Factory (Easy Version)

由于本题管道容量无限,所以所有的水要么被酿成了酒,要么从最后一个酒桶中流走了。

于是考虑一个很暴力的做法就是每次递推整个过程,然后求出最后一个酒桶流出的水量,拿总的水量减去这个求出的值就是最后的答案了。

\(dp_i\) 表示第 \(i\) 个点流出水量。

那么就有 \(dp_i=\max(dp_{i-1}+a_i-b_i,0)\)

每次都递推一遍是 \(O(nq)\) 的。

观察到这个转移可以写成矩阵乘法的形式,由于是 \((\max,+)\) 具有结合律,于是可以用线段树维护。

矩阵转移是

\[\begin{bmatrix} dp_{i-1}\\ 0 \end{bmatrix} \begin{bmatrix} a_i-b_i&-\infty\\ 0 &0 \end{bmatrix} =\begin{bmatrix} dp_i\\ 0 \end{bmatrix} \]

我们维护 \( \begin{bmatrix} a_i-b_i&-\infty\\ 0 &0 \end{bmatrix} \) 的区间乘积,然后每次单点修改,全局求乘积即可。

时间复杂度 \(O(n\log n)\)

CF1906F Maximize The Value

翻看别人 PKUWC 游记看到的题,简单题,放学回家路上想了几分钟就会了。

首先选择的是时间区间,但是我们要求的产生贡献的是包含序列上单点的位置区间。如果直接在时间区间内选择很难考虑到是否对于某个位置产生贡献。

所以很套路化地转化为对于位置进行扫描线,然后维护时间信息。对于一条在 \(t\) 时刻进行的更新 \((l,r,val)\),我们在扫描到 \(l\) 处加入,在扫描到 \(r+1\) 处删除。因此扫描到某个点时,目前维护的就是所有包含这个点的区间。

考虑处理询问,我们用线段树维护时间轴,每次加入就是在对于节点将权值设为 \(val\),删除就是将权值设为 \(0\)。然后查询时间区间内的子区间最大贡献,就是在时间轴上寻找区间最大子段和(注意上述扫描线已经保证了目前加入的区间修改都是肯定可以对这个点进行贡献的区间修改)即可。

于是我们用线段树维护区间最大子段和就行了。

时间复杂度 \(O((n+q)\log n)\)

P11505 [NordicOI 2018] Mysterious Array

一眼丁真秒掉的计数题,开心。

看到这个题想起来 ABC262Ex Max Limited Sequence。区别就是这题要求排列,那题没要求。

首先处理约束信息,因为要求的是区间最小值,所以我们可以用 set 处理出每个位置的值域下界,也就是所有包含该位置的区间约束的最大值。

然后对于一个最小值约束 \(\min=x\),可能存在若干对要求 \((l,r)\),由于这是一个排列每个数只能出现一次,但是必须所有区间都合法。所以 \(x\) 必然出现在这些区间取交得出来的 \([L,R]\) 中。

接下来考虑填入数字,对于每个位置是形如 \(a_i\ge x\) 的形式。如果按照序列顺序填入不好判断哪些数字用过了,所以我们考虑按照值域约束从大到小填入,这样子对于当前扫描到的数 \(x\),因为约束是 \(\ge x\)\([x,n]\) 之内的所有数就一视同仁了。先考虑确定 \(x\),就是在 \([L,R]\) 之内选择一个数,那么是 \(R-L+1\) 种方案。

接着考虑安排其他 \(\ge x\) 位置的答案,我们就是要在 \([x+1,n]\) 之内给他们分配(注意 \(x\) 这个时候已经被用过了),然后之前也用过若干个数字(所有满足值域下界 \(>x\) 的位置个数),设一共有 \(num\) 个,那么就是有 \(n-x-num\) 个数可以被我们利用,填入 \(y-1\) 个位置(\(y\) 是要求 \(a_i\ge x\) 的位置个数,由于已经填入了 \(x\),所以是 \(y-1\)),就是 \(A^{n-x-num}_{y-1}\)

时间复杂度 \(O(n\log n)\)

CF536D Tavas in Kansas

没啥好说的吧,这种的题就是观察约束条件,然后放到 dp 状态里。

可以发现对于每个人选的点是类似一个圆扩大的过程,不断选择离自己更远的一些点。于是离散化距离之后设 \(f_{i,j,0/1}\) 表示先手选了距离其 \(\le i\) 的点,后手选择了距离其 \(\le j\) 的点,现在轮到先手/后手。贡献函数也是很好在 \(O(n^2)\) 的时间内统计的,转移可以记录前缀最小值,\(O(1)\) 转移。所以时间复杂度 \(O(n^2)\)

CF1500C Matrix Sorting

\(n,m\) 同阶,以下时间复杂度均用 \(n\) 来表示。

可以发现这是一个稳定的排序。每次排序中值相同的时候,排名大小取决于上一次的排名。

因为后续的操作会影响到前面的操作,因此我们考虑反向思考这个过程,从末状态倒推。

考虑将初始矩阵中的每一行和某矩阵对应起来。然后给末矩阵增加一列,代表初始状态中每一行的排名(相当于第 \(0\) 次排序)。这样子我们就可以丢掉初始矩阵了,只用在末矩阵上思考。

如果末矩阵中某一列是单调不减的,那么它就可以当作最后一次操作的列。那么如果处理它其中的相等段呢,我们无法保证相等段就刚好排序成我们想要的,发现这个排序是稳定的,于是我们需要在倒数第二次操作以及之前的操作中将其变成单调递增的。于是倒数第二次操作就是满足在倒数第一次操作中相等段单调不减的。以此类推,最后进行第 \(0\) 次排序。

可是每次选择列的时候可能有多种选择,我们该如何处理。

结论就是:可以随便选一列满足要求的。证明很简单,

就以最后一个操作的选择为例来证明,假设有列 \(i,j\) 均满足单调不减,我们选择了 \(i\) 的时候必然不可能出现选 \(j\) 可以复原,但是选 \(i\) 无法排序完成。假设出现了以上情况,我们在选择了 \(i\) 之后,再选择 \(j\) 的成功排序操作方法,肯定也是能完成的。本质是我们随便选择了一列单调不减的序列之后,其实是弱化了原问题,比原局面更优了(因为单调递增的行的数量变多了),不存在走向了一个更劣的局面。

暴力寻找可行列的话,时间复杂度是 \(O(n^3)\) 的。可以用 bitset 优化,对于每一列 \(j\) 维护一个 bitset 表示 \(b_{i,j}\le b_{i+1,j}\) 的时候为 \(0\),否则为 \(1\)。每次寻找合法列对于某一列的 \(O(n)\) 判定可以变成 \(O(\dfrac{n}{\omega})\) 的按位与判定。于是时间复杂度就是 \(O(\dfrac{n^3}{\omega})\) 的。

还有一种更快的方法。上述方法其实是依赖于之前的操作,不断往前寻找之前的操作弥补后续的不足。另一种方法是一边倒退一边看看之后哪些操作能够掩盖当前操作的问题。

还是倒着做,假如我们要操作第 \(j\) 列,当前列上肯定有一些相邻行是满足 \(a_{i,j}>a_{i+1,j}\) 的,如果我们想要操作第 \(j\) 列的话需要这些行被之后的操作修正(如果是从倒着做的视角的话,就是那些已经选择了的操作)。在没有修正之前,这些行是 "锁" 着的。

我们建立二分图,左边为列,右边是行。如果 \(a_{i,j}>a_{i+1,j}\) 的话,代表 \((i,i+1)\) 是支配 \(j\) 的,于是我们连边 \(i\to j\)。如果 \(a_{i,j}<a_{i+1,j}\) 的话,代表对于第 \(j\) 列的操作能解锁第 \(i\) 行。进行一个特殊的拓扑排序,就是列必须要所有指向它的行都被访问才能进入队列,行只主要指向它的列中有一个被访问就能访问。以那些单调不减列为起点,然后最后看看第 \(m+1\) 列能否被遍历到,可以的话就输出逆拓扑序即可。

时间复杂度 \(O(n^2)\)

P2664 树上游戏

直接做很难,因此考虑贡献法。从每个颜色角度考虑,求出颜色 \(c\) 在几条从 \(u\) 出发的路径中出现过,对于所有颜色累加就是 \(sum_u\)

包含某个颜色有点难处理,再转化一下,可以改为求不含某颜色,只需要把那个颜色的点都去掉,形成多个连通块,给连通块内每一个点加上块的大小,代表从该点出发不包含某颜色的数量。

答案也就是总数减去不包含的数量。每个颜色单独处理显然重复处理信息了。因为这不是图是树,而树上每个点就是割点,连通块的产生应该是很容易的,只需要某个点的连或断所以我们应该是可以把所有颜色一起处理的。

假设 dfs 到了点 \(u\) 颜色为 \(c\),那么便会在其下方产生一个 \(c\) 的连通块,这连通块到哪里终止呢?就是 \(u\) 子树内某个 \(c\) 颜色为根的小子树,于是我们只需要给除去哪些小子树的部分加上 \(sz-\sum sz'\) 就行了,其中 \(sz\) 代表 \(u\) 子树的大小,\(sz'\) 代表根为 \(c\) 的小子树的大小。这个过程可以通过树上差分实现。\(sz'\) 也很好求,对于每个点开一个桶,每到一个点就在对应颜色位置累加,进出一个点前后某个颜色桶的差值大小就是那个颜色在内部出现的次数。

别忘记最后对于 \(1\) 点单独处理一下。

小技巧:如何找到子树中所有颜色为 \(c\) 的点:我们利用 vector 保存所有同一种颜色点,那么 \(c_u\) 对应的 vector 中所有 \(dfn_v\ge dfn_u\) 的点就是子树中颜色为 \(c_u\) 的点。

CF1824C LuoTianyi and XOR-Tree

首先有一个观察就是对于同一个子树下的叶子节点,他们到子树的根的时候,权值应该变成一样的。

于是设 \(f_{u,i}\) 表示将 \(u\) 子树内的权值全部调整为 \(i\) 的最小代价。

转移方程也很好写出 \(f_{u,i}=\min (\sum\limits_v f_{v,i},f_{u,j}+1)\)

\(+1\) 是代表我们可以在 \(u\) 这个点修改 \(a_u\) 的值,达到整体变化的一个效果。

可以发现其实所有的 \(f_u\) 值就分成两种,一种是最小值,还有一种是最小值 \(+1\)。于是我们直接维护所有能取到最小值的点的集合即可,不难发现这些点只有可能是从根到叶子节点一路异或出来的值。

每次在 \(u\) 点,我们启发式合并所有儿子的集合。合并出来的是一个可重集,看看哪种取值出现次数最多,就很显然为本次的最小值取值点(可能有多个)。

可以发现每次都调成最小值肯定是最优的,因为如果后续需要别的值,只需要 \(1\) 的代价就可以立刻变成别的值,而如果我们没取到最小值,本身就比最小值多花了至少 \(1\) 的代价。故直接取最小值,答案直接累加 \(son-num\) 即可,其中 \(son\) 是该点儿子的个数,\(num\) 是相同儿子最小值点的个数的最大值。

现在有一个问题就是启发式合并之后,如何清楚哪些非最小值点。反正我是没想到,看了题解证明才会的。就是如果 \(num=1\) 的话所有点都是最小值点就不用动了,如果 \(num\ge 2\) 的时候,我们暴力遍历集合,剔除掉非最小值元素还有把最小值的出现次数设置为 \(1\)。复杂度正确的原因是将出现次数 \(\ge 2\) 的最小值元素设置为 \(1\) 个至少将最小值个数减半,而同时清空了所有非最小值,所以整个集合大小至少也是减半了,这个最多进行 $\log $ 次。

时间复杂度 \(O(n\log^2 n)\)

P10200 [湖北省选模拟 2024] 花神诞日 / sabzeruz

想偏了啊,我做这题的想法和题解区的那篇二分图染色很接近,也尝试通过二元组的限制,用二分图染色和 2-sat 之类的处理,然后 \(2^{cnt}\) (\(cnt\) 为联通块数) 这样子计数。但是他后续的处理手法我完全想不到啊,也就卡住了。

换种想法。

由于题目是要求划分的两个集合的两两异或最小值分别 \(\ge k_1,k_2\)。所以本题肯定强于这个最小值的求解。

于是弱化点考虑如何快速求解一个序列中的两两异或最小值。有一个很经典的结论就是排序之后只有相邻两个数才可能作为最小值。

于是先对 \(a\) 排序,然后设计出 \(dp_{i,j,0/1}\) 表示考虑了前 \(i\) 个数,集合 \(0/1\) 选取的上一个数为 \(a_i\),另一个集合选取的是 \(a_j\)

于是 \(O(n^2)\) 的转移就很写了,这里仅讨论 \(dp_{i,j,0}\),因为对于 \(dp_{i,j,1}\) 同理。

  • \(j<i-1 \cap a_{i-1}\oplus a_{i}\ge k_1\)\(dp_{i,j,0}\gets dp_{i-1,j,0}\)

  • \(j=i-1\cap a_k\oplus a_i\ge k_2\)\(dp_{i,j,0}\gets \sum\limits_{k} dp_{i-1,k,1}\)

发现这个形式很像整体 dp,对于 \(j<i-1\) 部分继承,对于 \(j=i-1\) 部分进行修改。

考虑到 \(a_k\oplus a_j\ge k_2\) 的限制,我们可以用 Trie 树来维护这个整体 dp。具体来说固定 \(i\)\(0\)\(dp_{i,j,0}\) 的位置就保存在 Trie 树上的 \(a_j\) 的位置。

于是我们用两颗 Trie 树分别维护第三维为 \(0/1\) 的 dp 数组。

每次在 \(a_{i}\oplus a_{i-1}\ge k_1\) 的情况下继承,否则直接删除整个树。然后从另一颗树上进行一个子树求和,贡献到这颗树上。

时间复杂度 \(O(n\log V)\)

明明不难啊,怎么没想到。感觉还是卡在第一步的那个结论上了,想起来了这题就可以轻松解决了。

P7831 [CCO 2021] Travelling Merchant

这题也是无敌了。太巧妙了。

发现经过一条边之后我们的资产是不会减少的,所以有一个肯定能经过所有边的资产就是 \(\ge r_{\max}\),当然有这个还不够,我们需要有一个环才能实现无限行走。

设在 \(u\) 点可以无限行走的资产需要至少 \(dp_u\) 元。

于是以上综合两个条件,我们可以断定对于某个点 \(u\) 如果它的出边是当前权值最大的环上边,就可以确定在初始资产的为 \(r_{\max}\) 的时候可以进行无限行走。于是 \(dp_u\gets r_{\max}\)

这是其中一种更新方式,还有一种就是假设存在边 \((u,v,r,p)\),在 \(dp_v\) 已经确定的时候(也就是它的所有出边都已经把它更新完了),我们可以有 \(dp_u\gets \max(r,dp_v-p)\)

发现第二种更新方式有点像拓扑排序,于是我们可以采用类拓扑的方式进行更新。可以拓扑排序处理不了环啊,这个时候发现第一种更新方式就是去环啊,我们找到环上最大边并更新之后删掉,这不就完成了去环吗?

这就是这题的基本思路。关于实现,我们就是对于所有边排序,从大到小枚举每一条边,用这条最大边更新前,先"拓扑排序" 一下,去掉当前的所有非环边并更新 \(dp\) 数组,然后就可以判定当前的最大边是不是在环上了,如果在环上就更新后并删除,同时根据当前的 \(deg_u\) 来决定 \(u\) 点要不要入队。

P9521 [JOISC 2022] 京都观光

考虑 \((l,x)\to(r,y)\) 的路径可以 \((l,x)\to (r,x)\to (r,y)\),也可以 \((l,x)\to (l,y)\to (r,y)\)

前者的代价是 \(b_x(r-l)+a_r(y-x)\),后者的代价是 \(a_l(y-x)+b_y(r-l)\)。前者比后者更优需要满足

\[\dfrac{b_x-b_y}{x-y}<\dfrac{a_r-a_l}{r-l} \]

可以发现这可以放到两个凸包上求解。单调栈建立对于 \(a,b\) 的凸包之后,每次选择斜率写的一边走。由于上述式子可以放到到任意局部,所以这是对的。

其实本质就是对于两个凸包进行闵可夫斯基和。

XYD10171. Tspin double

每次对于单点操作或者对于一个区间操作任意多次,其实都可以按照从左往右的顺序操作线性 dp。这样子可以拿到 \(30\) pts。

考虑从高位往低位做,显然做过的位就可以舍弃了。

如果这一位所有数异或起来和 \(x\) 是一样的,那就不用变化,直接处理下一位就行了。

如果这一位是不一样,贪心思想我们只需要改变其中一个 \(a_i\) 那一位的值就行了。

如何寻找这个 \(a_i\),这里的处理非常巧妙,叫做 "去除最高位",我们将所有有当前处理的位的 \(a_i\) 都取反,这样子最高位就被消除了,同时 \(x\) 也需要取反。这样子代价是不变。去除最高位之后,每次选择最大的 \(a_i\) 即可。

UOJ61. 【UR #5】怎样更有力气

很 nb 的均摊题。

模仿 Kruskal 的过程,我们从小到大加边。每次都是尽可能地加边,对于没有限制的情况就是直接合并整条链。对于有限制的情况有点难处理,对于限制个数 \(<\) 链长的不会影响我们合并,可以忽略。直接采用树上并查集全部合并即可。对于限制个数 $\ge $ 链长的,我们可以暴力遍历这条链进行合并,每次要么合并两个集合,要么消耗掉一个限制,可以均摊掉。

于是我们需要在树上维护两个并查集,一个并查集是将 Kruskal 过程中已经到同一个联通块内的点放在一个集合里,注意这些点在原树上可能是非连续的。所以我们需要第二个并查集,如果在原树上 \(u\) 和其父亲已经在第一个并查集的同一个集合里面了,我们就将其合并,这样子能帮助我们在树链上快速跳,寻找联通块。

QOJ4217. Graph Coloring

错完了啊,我想遍了用三元环去做,用竞赛图缩点之后为链去做...... 还是不会。

有一个常见的 trick 就是我们假设自己已经确定了/知道了某个终态的一部分,如何求解答案。

这里我们固定某个点出边颜色的集合 \(s_u\)。如果存在边 \(u\to v\),那么所选择的颜色必须满足 \(c\in s_u,s\notin s_v\)。也就是说 \(s_u\) 不能是 \(s_v\) 的子集。

尝试给出一个没有包含关系的比较好的转化方式,我们可以发现如果满足所有集合大小相同且没有两个集合相同的时候是满足以上条件的。同时集合大小选择为 \(\dfrac{14}{2}=7\) 的时候是可以使得集合个数最多的。由于 \({14\choose 7}> 3000\),这题就做完了。

ABC277F Sorting a Matrix

手动模拟一下排序过程可以发现,如果对于行标号,每一行内的元素集合是不变的。对于列同理。

于是对于行排序只需要以 \((\min,\max)\) 为关键字对于行排序,然后看每一行值域是否重叠,这样子行的顺序就确定了。我们需要对于列进行排序使得每行中列对应的数值是单调递增的。

可以建图找环,不过直接做的话复杂度会爆掉,考虑使用虚点法就解决了。以权值为虚点,所有权值为该值的点都连向虚点,虚点之间是一个后缀传递建边关系。

XYD10141. 水晶

直接进行统计是很难处理多项式的 \(k\) 次幂的,但是我们会二项式定理,因此考虑将 \(\sum\) 的多项转化为 \((a+b)^k\) 的二项,这启发我们用树形 dp 处理,因为合并两个数组就是二项了。

思考一下 dp 的条件约束,我们需要知道距离 \(u\) 点最近的异色点。可以设 \(dp_{u,i,0/1}\) 表示 \(d_u=i\) 的时候的贡献和,其中 \(0/1\) 分别代表目前有没有相邻点满足 \(d_v=d_u-1\),或者 \(d_u=1\) 的时候需要一个相邻异色点。如果没有在子树内完成这个约束,就要求 \(u\to fa\) 的时候,遇到 \(0\) 的话,必须满足 \(d_{fa}=d_u-1\)

记录 \([0,k]\) 次幂,采用二项式定理合并 dp 数组,时间复杂度 \(O(n^2k^2)\)

posted @ 2024-11-27 20:06  Mirasycle  阅读(20)  评论(0)    收藏  举报