构造题

参考资料

大部分还没看。
构造题方法总汇
构造 专题整理
构造题
构造方法(容易发现/显然/不难注意到/不难证明)
构造题专项训练
算法竞赛如何训练构造题 非常好的文章。

知识介绍

前言

练习构造题,关键是不要害怕,只要你不半途而废,逐渐就会找到感觉。

虽然但是,总结积累一定的思考方式/转化技巧也是非常必要的。

简介

通常意义下,我们认为难以转化为任何已知算法来解决,并且具有较高思维难度的题目为构造题。也就是我们要直接得到题目要求的结构。

这类题特殊在答案不会很复杂,但如果之前没有见过类似模型就会无从下手。给出的感受大概是,我们猜出了一个模型,并且这个模型恰好就是对的,但我们并不知道这个模型是怎么来的。

大多数构造题还有一个重要特点,就是非构造题通常指向同一个答案/结构,我们要做到某种意义上的最优化。但构造题只需要我们给出一个满足条件的答案,不同的思考方式可能指向不同的正确答案。

如何思考构造题

以下来自课件内容:

上面提到,构造题大多有不唯一答案。对解题者来说,当然希望找到一个形式尽可能简单或尽可能满足更多特殊性质的解。

一个比较重要的思维方式是我们可以试图自己为解增加一点限制来换取切入点。(当然,也有可能由于添加的限制过紧导致无法得到解。)

实际上,算法竞赛中的构造题,除了极少部分很偏的知识点外,都可以被拆分为几个核心解决方式。

比较常见的技巧有下面几类,也可以在做题学习过程中不断总结积累。

  • 增量构造法。也就是先假设可以构造出大小为 \(f(n)\) 的解,然后再拓展到 \(n\) 的解。
  • 中转构造法。常用于有操作并且 操作可逆 的题目,如果希望我们由 \(A\) 构造到 \(B\),可以选择中间状态 \(C\) 并构造 \(A \to C\) 和 \((B \to C)^{-1}\)。
  • 猜测范围法。对于一些无从下手但要求最大/最小次数的题目,可以考虑猜测答案的范围。
  • 图论建模法,常建模型有欧拉回路和网络流,也有各种与环/匹配相关的问题。
  • 分治法。与增量构造比较类似。

训练技巧

  1. 把条件翻译成硬约束和自由度。

  2. 学会从小样例中寻找规律,找到不变亮还有可扩展的结构。

  3. 先判断“不可能”,再考虑“怎么构造”。存在性条件往往就是构造方向本身。比如说:一旦你证明“只有当 n 为偶数时可行”,那构造大概率就要围绕“成对处理”展开。

  4. 练“修补能力”,而不是只练一步到位。调整技巧:交换、翻转、补对、拆块重组、引入缓冲位置。训练时要刻意问自己一句:如果现在只差一个条件不满足,我能改哪里,代价最小?

  5. 把证明当成构造的一部分,在构造的过程中同步考虑为什么成立,一份可比赛使用的构造题思路,至少要能回答三件事:为什么满足所有条件,为什么不会产生冲突,为什么能覆盖所有合法输入范围。你不需要把每道题都写成长证明,但至少要能用几句清晰的话把核心因果讲清楚。

  6. 要经常回炉。

trick

  1. 对于排列交换问题,逆序对数的分析非常有效,常能用来辅助判断无解。

  2. 涉及神秘操作的构造排序题,要想办法将其转化为基础的操作,这样会好做很多。

  3. 对于排列,我们要思考怎么将元素归位,最经典的就是让 \(p_i,i\) 产生关系,连边之类的。

  4. 排列问题判断无解,用逆序对来证明非常好用,在证明逆序对奇偶性的时候,很多时候要对长度设元,在通过一些分类讨论分析。

  5. 中转构造的思想,如果直接处理难以处理,考虑设计中间状态;

  6. 考虑最小和最大两个特殊部分有没有什么神奇性质。

  7. 选择用叶子往上去确立,而不是用重心,因为每个节点只有一个父亲,而可能有多个儿子。

  8. 对于最小化操作次数,猜测答案一定不会很大。

  9. 对于不等式条件的处理,移一下项,将有用的信息放在一边。

  10. 对于这种 \(i\) 对应着 \(a_i\) 这种类型,这两类之间考虑连边,转化成图论上的相关问题,就像排列中的置换环一样,还有二分图匹配一样

题目

CF1882E1 Two Permutations (Easy Version) \(\checkmark\)

题目大意

有两个排列 \(a\)(长度 \(n\))和 \(b\)(长度 \(m\))。每次操作选择 \(i,j\),将 \(a\) 变为 \(a_{i+1},\dots,a_n,a_i,a_1,\dots,a_{i-1}\),对 \(b\) 同理。目标将 \(a,b\) 都变成各自的恒等排列。求任意不超过 \(10000\) 次操作的方案,无解输出 \(-1\)。
\(1\le n,m\le 2500\)。

解题思路

首先这道题可以大致分为两个部分:

  1. 如何将一个给定排列排序;
  2. 如何让两个排列同时排好序。

我们先来解决第一个部分:如何排序?

一般来说,对于这种通过神秘变换排序,我们都希望其能转化为一些简单变换,最常见的就是转化成交换两个数。这道题同理,我们发现,使用三次变换便可交换任意两个数,比如下面我们希望在不影响其他数的情况下,交换 1 和 2,初始为 A 1 B 2 C:

  1. B 2 C 1 A
  2. C 1 A 2 B
  3. A 2 B 1 C

我们每次交换两次代价是 3,交换 \(n-1\) 次代价为 \(3n-3\) 便可使排列有序。

现在来解决第二部分:如何同时排好序?

这种问题很显然,两次变换就可以与原来不变,需要两边操作的奇偶性相同,再转换一下,就是逆序对个数奇偶性相同。

首先如果他们操作次数差值是偶数一定有解,现在考虑是奇数的情况。如果 \(n,m\) 其中一个为奇数有解,否则无解。

考虑证明,需要知道进行一次操作奇偶性的变化情况:

假设选择 \(i\) 进行变换,A i B 变成了 B i A,\(A,B\) 间逆序对个数的变化为 \((i-1)(n-i)\),\(A,i\) 和 \(B,i\) 间的贡献为 \(n-1\)。奇偶分析一下可知,\(n\) 为奇数可能发生变化,偶数一定会发生变化。

当 \(n/m\) 为奇数时,每次选 \(i=1\) 即可完成构造。

更进一步,其实还可做到 \(3n\) 步以内构造,因为操作次数与逆序对数的奇偶性有关,我们只需要在开头数一下逆序对个数,判断是否有解即可。如果差值是奇数,其中 \(n/m\) 是奇数,只需要对奇数那个做一遍 \(i=1\) 的变换,另外一个正常交换,边可以做到 \(3n-2\) 步以内。

启发:

  1. 涉及神秘操作的构造排序题,要想办法将其转化为基础的操作,这样会好做很多。
  2. 这种排列问题判断无解,用逆序对来证明非常好用,在证明逆序对奇偶性的时候,很多时候要对长度设元,在通过一些分类讨论分析。

P16325 【MX-J29-T4】XOR and Swap \(\checkmark\)

题目大意

给定两个 \(0\sim 2^n-1\) 的排列 \(p,q\),下标从 \(0\) 开始。每次操作选择不同下标 \(i,j\),若满足 \(p_i\oplus p_j\le i\oplus j\),则交换 \(p_i,p_j\)。要求在不超过 \(2.1\times 10^6\) 次操作内将 \(p\) 变为 \(q\),输出任意一组操作方案。
\(1\le n\le 20\)。

解题思路

这题只有青?
首先,直接转有些困难,但是我们发现操作是可逆的,就是 \(p_i,p_j\) 交换后还可以再次交换回来,也就是 \(p\to q\) 的操作逆过来就是 \(q\to p\)。不妨设置一个中转站,比如说排好序的序列 \(S\)。这样我们就只需要算出 \(p\to S\) 和 \(S\to q\) 的逆操作 \(q\to S\) 即可。

现在要求 \(n\) 步做到将序列排好序,与上一题类似的,我们考虑将其转化成普通交换操作。我们 注意到:每次希望通过一次交换将一个元素还原。那么就是要找到 \(p_j=i\) 且 \(p_i\oplus p_j\le i\oplus j\),带进去就是 \(p_i\oplus i\le p_j\oplus j\),于是我们直接用小根堆维护 \(p_i\oplus i\) 即可。

启发:

  1. 运用了中转构造的思想,如果直接处理难以处理,考虑设计中间状态;
  2. 还是和上题类似,我们要思考怎么将元素归位,最经典的就是让 \(p_i,i\) 产生关系,连边之类的。

[ARC103F] Distance Sums \(\checkmark\)

题目大意

给定 \(N\) 个互不相同的整数 \(D_1,D_2,\dots,D_N\),问是否存在一棵 \(N\) 个顶点的树,使得第 \(i\) 个顶点到其它所有顶点的距离之和恰好为 \(D_i\)。若存在,构造出任意一棵满足条件的树;否则输出 \(-1\)。

\(2\le N\le 10^5,\ 1\le D_i\le 10^{12}\),所有 \(D_i\) 互不相同。

解题思路

先考虑判定,直接换根 DP 就行了,有没有什么启发呢?有的,就是我们可以仿照换根 DP 那种贡献转移方式,当然这要确立一个点之后用。

那有没有什么点是一定确立的呢?首先最小的一定是重心,其次,最大的一定是叶子。有两个点是确立的,并且我们希望能通过确立的点确定其他点。我们选择用叶子往上去确立,而不是用重心,因为每个节点只有一个父亲,而可能有多个儿子。

那构造过程就呼之欲出了:现在确立了一些点,如果当前考虑的点能作为若干个点的父亲,那就加上去,否则作为新的叶子节点,判定 \(u\) 是否能作为 \(v\) 父亲时看看 \(D_u=D_v+siz_v-(n-siz_v)=D_v+2siz_v-n\) 即可。具体实现时就是枚举每个点二分找父亲。

启发:

  1. 自己想的时候只想到了最小是重心,没有想到最大是叶子,所以比较难做,以后一定要考虑最小和最大两个特殊部分有没有什么神奇性质。
  2. 选择用叶子往上去确立,而不是用重心,因为每个节点只有一个父亲,而可能有多个儿子。

CF1720E Misha and Paintings \(\checkmark\)

题目大意

有 \(n\times n\) 矩阵 \(a\),每次操作选一个正方形子矩阵,将其内所有元素改为任意 \(1\sim n^2\) 的正整数。求使矩阵中不同元素个数恰好为 \(k\) 的最少操作次数(可为 \(0\))。

\(1\le n\le 500,\ 1\le a_{i,j},k\le n^2\)。

解题思路

首先有一个简单的结论:如果原本的元素个数小于 \(k\),那么最优方案只能是 \(k-cnt\)。

然后现在是大于,我们大胆的去猜测答案范围不会太大,手摸一下发现不会超过 2,考虑证明。

固定左上角在 \((1,1)\),每次去延伸扩展,找到包含个数 \(\le cnt-k\) 的最大正方形 \((1,1)\to (i,i)\),然后用第二个正方形固定右下角为 \((i+1,i+1)\) 去扩展左上角,第二个正方形的扩展每次最多增加两个颜色数,故两个正方形肯定能包含颜色个数 \(cnt-k\) 或者 \(cnt-k-1\),又由于有赋值操作,所以这两个都可以,故两次操作一定能满足答案。

现在问题就弱化成能不能找到一个正方形 完全覆盖 \(cnt-k\) 种颜色,对每种颜色处理出一个能覆盖它的最小长方形。枚举正方形长度,对每种颜色处理出有哪些左上角可以覆盖这种颜色,做二维差分和二维前缀和统计答案即可。

启发:

  1. 对于这种最小化操作次数,猜测答案一定不会很大。
  2. 要想到动态扩展,去证明答案上界。
  3. 正方形的限制比较紧,不需要像矩形一样枚举左上角和右下角,只需要枚举长度,会简单一些。

CF1270G Subset with Zero Sum \(\checkmark\)

题目大意

给定 \(n\) 个整数 \(a_i\),满足 \(i-n \le a_i \le i-1\)。求一个非空下标子集,使对应的 \(a_i\) 之和为 \(0\)。保证有解,输出任意一组。

\(1\le n\le 10^6\),\(i-n\le a_i\le i-1\)。

解题思路

这个限制非常的奇怪,所以考虑从这个限制入手,直接做比较难做,考虑移一下项变成 \(1\le i-a_i\le n\),然后就是人类智慧时刻,\(i\to i-a_i\) 连一条有向边,由于每个 \(i\) 都只有一条出边,所以一定会形成内向基环森林。

每一个环都对应着一个答案,这是因为在一个环上,\(\sum i=\sum (i-a_i)\),所以 \(\sum a_i=0\)。

启发:

  1. 对于不等式条件的处理,移一下项,将有用的信息放在一边。
  2. 对于这种 \(i\) 对应着 \(a_i\) 这种类型,这两类之间考虑连边,转化成图论上的相关问题,就像排列中的置换环一样,还有二分图匹配一样。

CF804E The same permutation \(\checkmark\)

题目大意

给定 \(n\),考虑排列 \(1,2,\dots,n\)。需要对所有满足 \(1\le i<j\le n\) 的位置对 \((i,j)\) 恰好执行一次交换操作(顺序任意),判断是否能使排列最终保持不变;若可以,输出 YES 及任意一种操作顺序,否则输出 NO。
\(1\le n\le 1000\)。

解题思路

这题的思想比这题本身更有价值。

首先考虑无解地判断。共有 \(n(n-1)\over 2\) 次操作,每次操作一定改变逆序对的奇偶性,所以这要求 \({n(n-1)\over 2}\bmod 2=0\),所以 \(n\bmod 4=0,1\),否则无解。

我们可以先手玩出 \(n=4\) 或者 \(n=5\) 的解

找到了与 4 的关系,接下来有两种方法,一种是增量,另外一种是分组。

增量

假设 \(n\) 已经有一组合法答案了,考虑将 \(n\) 拓展到 \(n+4\) 的情况。

这个做法相对于分组的做法需要更多的手玩和观察,因为不完全能借助爆搜。

考虑拿出最后 4 个元素记为 \(a,b,c,d\),我们希望用完 \(a,b,c,d\) 内部的交换和 \(a,b,c,d\) 与前 \(n-4\) 个元素的交换并且保持不变,现在考虑怎么消耗掉与前 \(n-4\) 个交换,手玩一下发现,第 \(i\) 个位置消耗完可以使得 \(1\sim i\) 整体循环 右移/左移 一位,进一步的其实可以省掉中间一部分数只进行 \(1\sim n\) 还有 \(i\) 之间的移动,于是就可以进行类似下面的构造过程。

1 2 3 4 ... a b c d

1 2 3 4 ... a b c d 右移开始
a 2 3 4 ... 1 b c d
a 1 3 4 ... 2 b c d
a 1 2 4 ... 3 b c d
...
a 1 2 3 ... n b c d 右移结束,左移开始
a 1 2 3 ... b n c d
a 1 2 ... n b n-1 c d
...
1 2 3 4 ... b a c d 左移结束
c 1 2 3 ... b a n d 类似的
1 2 3 4 ... b a d c

可以构造出一组交换 \(a,b\) 和交换 \(c,d\) 时覆盖前面全部对换的解,在原来后四个交换过程中遇到需要 \(swap(a,b)\) 或者 \(swap(c,d)\) 的操作是加进去。

分组
  • \(n\bmod 4=0\),四个分一组,只需要考虑组间怎么跑即可,可以用程序爆搜跑出个 \(4\times 4\) 的交换情况即可。
  • \(n \bmod 4=1\),初步的想法是四个一组,最后在 5 个一组,但是爆搜跑不出 \(4\times 5\) 的交换情况。改一改,变成 4 个一组,最后单剩一个的情况即可,考虑一个与四个之间其实就是 5 个的情况,所以可以完成。

启发:

  1. 增量、分组思想的具体体现;

  2. 很多时候可以用计算机辅助暴搜计算。

posted @ 2026-09-20 14:33  lcx_OIer  阅读(20)  评论(0)    收藏  举报