构造题
max 言:对于构造类题目,如果题目给的自由度太大,可以尝试自己增加一些条件,然后再去做。更常见的是直接打表找性质。
2025.11.13 T1 Wall
考虑 m = 0,构造三个序列凑一凑,然后在前面顺次填数,注意两段之间不能有贡献
2025.11.4 T3
考虑倒着子序列自动机,写出式子后倒推,发现最开始的地方如何分解不确定,相信随机分解神力。
2025.10.20 T1
观察到序列 2 增长地飞快,所以要构造很多 1 去抑制增长速度。
agc_052_a
构造与输入无关。可能需要一点一点分析题目性质然后画决策树分析。
agc_050_a
第一反应是线段树。(其实按位考虑说不定对于某些题也是一种突破口)
正解是连 \((2*p)-1\bmod n+1)\) 和 \((2*p+1)-1\bmod n+1\)
然后发现对于每个点的(虚)子树内,叶子节点的个数就够了。线段树内横向走编号是 +1
应该从每个点均匀分布的角度出发。
CF2048E
两个启示
- 证明下界构造下界。很多构造题要先思考什么时候有解。
- 循环移位构造法。
CF1951D
如果 (n - k + 1) * 2 > n , 可以设置一个 n - k + 1 的摊位和一个 1 的摊位,游戏结束。
然后发现剩下的情况特殊处理一下即可。
启示:对于构造题,可以对特殊的情况先想一种构造,说不定就做出来了。
[AT_agc068_b AGC068B] 01 Graph Construction - 洛谷
这种题一定要多手玩,理解每个操作的实际意义。
首先发现 \(n<< |S|\) 所以构造得可以比较松。
可以让 s 的前 n 个是 0,t 的前 n 个是 1。
然后考虑 s t 后入一个 0,相当于排列循环移位一下;如果 s 加 1,t 加 0,等价于 当前两个排列的首位连边并且删掉这一位。
发现这样就能构造出来答案了。
[AT_agc059_d AGC059D] Distinct Elements on Subsegments - 洛谷
虽然理解的非常浅,但是还是可以口胡一下我的感悟。
首先观察一些 b 的必要性质:\(|b_i-b_{i+1}| \le 1\),证明显然。但这就是第一个突破口。
设 01 变量 \(l_i\) 表示 \(a_i\) 与向左走 k 个数中的每个数都不同,\(r_i\) 表示 \(a_i\) 与向右走 k 个数的每个数都不同,于是又有:
当 b 不同的时候 l 和 r 是可以确定的,b 相同的时候 l 和 r 是相同的但是不知道具体是什么。
接下来的事情就是找 l 和 r 必须满足的其他性质并且尽量凑出一组合法的 l 和 r 数组,最后还原 a,具体不是很会。
[AT_agc066_a AGC066A] Adjacent Difference - 洛谷
鸽巢原理。
启示是可以想两个或多个(类似)的构造,然后看当前用哪个满足题意。
“鸽巢原理一般用于有多个备选项,它们从总体来看有某个数量关系,可以导出存在一个个体满足特定条件。偶尔也反过来用证明无解。在实际应用中,备选项可能是给定模型的局部,也可能需要自己构造,后者会比较难,关键在于找到可能性足够少的“鸽巢”。”
类似的:CF1450C2 Errich-Tac-Toe (Hard Version) - 洛谷
这个题就是考虑给棋盘三染色。因为任意的连续的一排和一列的三个都是不同的颜色,可以直接让所有 0 颜色是 X,1 颜色是 O。当然也可以让 1 颜色是 X,2 颜色是 O,或者让 2 颜色是 X,0 颜色是 O。
考虑原图中每个棋子只会在三种操作中的一种会需要被操作到,所以三个操作的操作次数总共是 k,于是最小的那个就小于 k/3。
类似的:[P9820 ICPC 2020 Shanghai R] Mine Sweeper II - 洛谷
把雷和空地互换,数字之和不变。
P12417 基础构造练习题 1 - 洛谷
以下题解复制自不屈的三叶虫。
首先来考虑这样一个问题:我们现在手上有数 \(a_1,a_2,a_3,a_4,\cdots,a_{t-1}\) 各两个,另有 \(a_t\) 四个,我们要如何构造一个比较好的操作次数来完成题目的要求?
记 \(x=a_t\)。我们先把三个 \(x\) 放着不动,一个 \(x\) 依次和 \(a_1,a_2,\cdots,a_{t-1}\) 操作。此时我们剩下的数形如:\(x,x,a_1x,a_1a_2x,\cdots,(\prod_{j=1}^ia_j)x,\cdots,(\prod_{i=1}^ta_i),(\prod_{j=1}^ta_i),a_1,a_2,\cdots,a_t\)。这一步一共 \(t-1\) 次操作。
记 \(y=\prod_{i=1}^ta_i\)。我们令 \(y\) 依次和 \(a_t,a_{t-1},\cdots,a_1\) 操作。此时我们剩下的数形如:\(x,x,a_1x,a_1a_2x,\cdots,(\prod_{i=1}^{t-2}a_i)x,y,xy,a_{t-1}a_ty,\cdots,(\prod_{j=i}^ta_j)y,\cdots,(\prod_{i=1}^ta_i)y,(\prod_{i=1}^ta_i)y\)。其中最后两项实际上就是 \(y^2\)。这一步一共 \(t\) 次操作。
\(\forall 1\le i\le t\),将 \((\prod_{j=1}^{i-1}a_j)x\) 与 \((\prod_{j=i}^ta_j)y\) 操作,得到两个 \(xy^2\)。这一步一共 \(t\) 次操作。
此时场上不为 \(xy^2\) 的数还有 \(x\) 和 \(y^2\) 各一个,操作一次即可。这一步一共 \(1\) 次操作。
以上加起来一共 \(3t\) 次操作。
回到原题,\(n\) 为奇数时显然无解(因为 \(n>1\)),\(n\le 2\) 显然特判,接下来考虑 \(n\) 是 \(\ge 4\) 的偶数,记为 \(2t+2\)(\(t\ge 1\))。
对于 \(1\le i\le t+1\),我们对 \(a_{2i-1}\) 和 \(a_{2i}\) 操作,这一步一共 \(t+1\) 次操作。接下来我们对 \(a_{n-3}\) 和 \(a_{n-1}\) 操作,\(a_{n-2}\) 和 \(a_n\) 操作,这一步一共 \(2\) 次操作。加起来一共 \(t+3\) 次操作,此时我们把问题化归为了一开始解决的那个。于是一共 \(4t+3\) 次操作,我们在 \(2n-1\) 步内对原问题完成了解决。
代码对着这个东西实现就可以了。
CF1364D Ehab's Last Corollary - 洛谷
max 好像讲过。
这个套路是抓一个生成树出来分析。因为 dfs 生成树只有树边和返祖边,性质比较好。
如果返祖边构成的环小于 k,就输出。
否则,如果原图是树,就黑白染色(二分图)。
否则,一定有返祖边,且返祖边构成的环小于 k,说明树高至少为 k,可以隔一个选一个,可以选出 k/2 个点。
[P5811 IOI 2019] 景点划分 - 洛谷
同样是 dfs 生成树的套路。
肯定是让小的那两个集合联通更好,称其为 a,b,有 \(a\le\frac n3,b\le\frac n2\)。
先考虑树怎么做,这等价于找一条树边满足两端的子树大小分别大于 a 和 b。
考虑在重心上操作。
如果重心的任何一颗子树大小都小于 a,那么划分 a 子树的时候就一定要占用重心,这样就一定不可能划分出一个更大的集合 b,所以无解。否则直接把一颗子树大小大于等于 a 的树整个划分到集合 a。由于重心的性质,这个子树的大小小于 n/2,所以剩下的一定大于 n/2 所以大于 b。
考虑回到原问题,现在加入了一些返祖边,但是没有横叉边,如果把重心祖先所在的子树叫做 T,那么只有 T 能进行拓张。
如果没有大小大于等于 a 的子树,并且 T 完全拓张以后也小于 a,那么肯定无解。对于剩下的情况,也就是没有子树大于 a 但是 T 可以拓张到大于 a 的情况,可以按照任意顺序拓张 T,直到大于 a 停止。考虑进行最后一步前,T 小于 a,最后一步拓展的子树也小于 a,所以 T 小于 2a。因为 \(b=n-a-c\le n-2a\),所以剩下的部分大于 b,构造完成。
Baggage
下文摘自《信息学竞赛中构造题的常用解题方法》
题意:
有 \(2n\) 个包裹,其中有 \(n\) 个 \(A\) 类包裹,和 \(n\) 个 \(B\) 类包裹,初始时它们的排列如下:
\(B\, A \,B\, A\, B\, A \cdots B\, A\)
这些包裹占据了编号为 \(1\) 到 \(2n\) 的格子,同时还有编号为 \(−2n + 1\) 到 \(0\) 的 \(2n\) 个空格子可供使用。现在要将这些包裹重新排列,使得它们形如\(A\, A ... A\, B ... B\, B\) 即,这些包裹占据了相邻的 \(2n\) 个格子 (不一定是 \(1\) 到 \(2n\) ),且所有的 \(A\) 类包裹在所有的 \(B\) 类包裹的左边。
排列过程由若干次操作组成,在每一次操作中,可以选择相邻的两个包裹 (不能只选择
一个),并将它们移动至某两个相邻的空格中。
给定 \(n\) ,找到一个最短的操作序列。
\(3\le n\le 100\) 。
题解:这种要求最优方案的题往往两个步骤:证明下界;构造下界。
与通常构造题不同,本题要求的是最短的操作序列,看起来难以下手。但经过一些尝试,或是对 \(n\) 较小的情况进行搜索,会发现它们的最短序列的长度都是 \(n\) 。
事实上,证明最操作数次数不少于 \(n\) 是容易的:考虑有多少对相邻的包裹类型相同,设这个个数是 \(d\) ,初始时 \(d=0\) ,而在结束局面中 \(d=2n-2\) 。在一次操作中,取出包裹时不会使 \(d\) 增加,而在放回包裹时,假设放的位置是 \(t\) 和 \(t+1\) ,则只可能增加 \((t-1,t)\) 和 \((t+1,t+2)\) 这两对相邻的包裹。同时容易发现,第一次操作至多使得 \(d\) 增加 \(1\) ,因此总的操作次数不少于 \(1+\lceil\frac{2n-3}{2}\rceil\) 。
接下来,我们就要尝试对所有 \(n\) 构造长度为 \(n\) 的操作序列。对于 \(n\) 较小的情况,我们可以直接利用搜索求出操作序列。经过观察发现,在 \(n>3\) 的情形中,我们都是将这些包裹从 \(1\sim 2n\) 的格子移植编号为 \(-1\) 到 \(2n-2\) 的格子。
因此,我们可以定义函数 \(solve(n,x)\) 表示将 \(x+1\) 到 \(x+2n\) 的格子(这些包裹形如 \(BABABA\cdots BA\) )移植编号为 \(x-1\) 到 \(x+2n-2\) 的格子,并排列成 \(AA\cdots AB\cdots BB\) 。
通过尝试,或是对 \(n\) 稍大一些的情形的观察,对于 \(n\ge 8\) 的情况,我们可以构造如下操作序列(其中 _ 表示空格子):

在这里我们发现最后一行的红色部分正好符合 solve 函数的输入,因此我们调用 solve(n-4,x+4) 对其进行递归求解。

至此,我们便完成了长度为 \(n\) 的操作序列的构造,解决了此题。
其实递归法很多时候不会是一次做到位的,有可能前面先做一些,可能还没到达终态,但是找到子问题先递归下去,然后再在回溯的时候变成终态就可以了。
也就说我们要只要找到一种到达子任务的方法,然后再把再找到子任务的区间变成终态后,该区间怎么变成终态的方法,最后考虑基底就可以了。(这应该是一般的解题思路和方法。)
CF1470D Strange Housing - 洛谷
同样是递归子问题。
图不联通必定无解,考虑归纳地构造证明连通图有解。
所谓归纳证明,就是在联通图上添加 n 号节点怎么搞,发现直接贪心地看这个点是否能作为关键点即可。具体的,如果它连向的点有关键点,它就不是关键点;如果它连向的点都是非关键点,那么它就是关键点。
可以在 dfs 生成树上完成此过程。

浙公网安备 33010602011771号