《构造类问题的若干解题方法》授课题目总结
APIO2025授课内容,讲者范斯喆。
鲜花
妹法去a派哦好气啊,为啥是 sorted by noip。。。而且据说今年寒假那次是最后的高校营,因而心心念念的 PKUSC 也爆炸了(竞赛停课天数 \(=0\))。。。不过也该知足了,去过俩次 WC(虽然是一铁一铜),然后还拿了个钩八,然后P营优秀diao用没有。不管了,至少还能云参加 APIO 看看讲了什么有意思的东西。
有部分题目因为时间不够没研究,先把帖子抢救完有机会再来完善qwq。
调整法
也就是通过无视条件/新增条件等方法先拿出来一个符合部分要求的解,然后再去调整自己的做法,或者直接用一些算法调整初步得到的解。
对这个调整法印象最深刻的题,也是接触到的第一个题,应该是 [省选联考 2021 A 卷] 矩阵游戏。这个题基本上就是最典的调整了,而且思维量也不小,算是很好的题目。
【IOI2020】Stations
给你一棵树,初始标号仅用于输入,你需要进行重标号告诉交互器,然后你会忘记这棵树,但是我会给你 \(T\) 组询问 \((s,t,c)\),其中 \(s,t\) 是某两个点的标号,\(c\) 为 \(s\) 的所有邻域的点的标号,你需要回答我 \(s\to t\) 的路径与 \(c\) 的交集所对应的那个点的标号。
\(n\le 1000\),你的标号必须是一个 \(0\sim n-1\) 的排列。
其实比较显然的事情是可以按照 DFS 序标号。but 窝们无法区分外界和 DFN 序最后一颗子树,因为窝们没 size。原题其实没有给标号时排列限死,只要标号不重也可以。所以窝们可以每个标号 *=(n+1) 然后再加一个 size 上去,就可以获得 \(22\) 分的佳寄。
但是我们容易观察到最大的损耗在于 \(s\) 节点编号根本没用上,but子树内又恰好还缺一个点的信息。
于是我们就想要进行奇偶划分,朴素的设想是,奇数层我们直接存 \(\max\limits_{i\in subtree(x)} dfn_i\),也即出栈序,偶数层则存 \(\min\limits_{i\in subtree(x)} dfn_i\)。那么首先 \(s\) 在奇数层肯定能做了。偶数层发现也可以做,相当于我作为左端点,我的儿子作为一堆子树的右端点,恰好框出来了每个子树的范围。
但是有一个问题,标号会重复。这个怎么办呢?我们考虑从下往上编号,如果在偶数层遇到和之前一样的编号 \(x\),我就检查 \(x-1\) 是否可用,然后是 \(x-2\),以此类推,直到找到一个没用过的。奇数层同理,我也去检查 \(x+1\)、\(x+2\) ……
还有一个非常人类智慧的地方是,反正树的标号就几种,我一个个试一试或者把一些方法拼起来,总能得到一个可用的标号方式的吧,然后说不定就试出来入栈出栈序了。
常见树的标号方式
Summarized by Deepseek
-
普通树
DFS 序、BFS 序、树剖序、入/出栈序(子树最小/大DFS序)、欧拉序(每次进入/退出某点都做记录,长度为 \(2n-1\))。
-
二叉树
先序、中序、后序、二叉堆标号。
Interactive Mex Tree【未写】
讲烂了,好像是第4次见到这个b题了。不写,也是入栈出栈序刻画一类结构。
现在全国人民都知道这个题了,所有入栈出栈的题都不会拿出来考了,白学嘿嘿
上下界法
也就是拿出来一个可以构造的上下界,然后证明上下界中间的每个值都可以被取到并构造出调整方法。
CF1311E Construct the Binary Tree
构造 \(n\) 个结点,且以 \(1\) 为根的深度和为 \(d\) 的二叉树。
无解输出 \(-1\)。\(n\le 5000\)。
据说可以线性。
考虑下界是 \(O(n\log n)\) 的,上界是 \(O(n^2)\) 的。
我们先拿到下界,然后一点点使得 \(\sum dep_i\) 每次修改然后增加 \(1\)。
具体地,我们找到一个叶子 \(i\),并且使得他有一个同深度的点 \(j\),并且 \(j\) 度数 \(\le 2\)。然后把 \(i\) 挂过去。
那么其实简单维护叶子和度数 \(\le 2\) 的点的集合,然后直接 \(O(n^2)\) 强力构造即可。
考虑是否会构造到中间出现找不到点的尴尬情况。
如果存在某叶子同层的点没有两个儿子,那一定是可以找到的。
那么除了链这种每个点都只有一个儿子的情况,只要有一个点有两个儿子,那么他子树内就必然有至少两个叶子,而叶子的同层都必须要有两个儿子,那么只会创造出更多的叶子, 总之最后肯定就可以找到了。感性理解,不会证明。
Puzzle: Nurikabe【未写】
据说唯一网上能找到的题解只有三行。摆。
CF1770H Koxia, Mahiru and Winter Festival
\(n\times n\) 的无向网格图上需要摆放 \(2n\) 条路径,分别从 \((1,i)\) 出发前往 \((n,p_i)\)、\((i,1)\) 出发前往 \((q_i,n)\)。\(p,q\) 都是排列。
询问每条网格边的最小最大覆盖次数并构造方案,\(n\le 200\)。
由鸽巢原理可知只有 \(p_i=i, q_i=i\) 时答案为 \(1\)。
显然如果我们以这个情况开始调整实在是过于 naive。所以我们考虑 \(p_i=q_i=n-i+1\) 的时候如何构造方案。其实这里也有点聪明的元素在,不过能想到调整法就很大胆了,况且这还是黑题,我们就不去评价究竟是如何想到的了。
构造这个其实还算简单,瓶颈在于考场无法带入水彩笔,导致了少数在电脑上用画板居然比草稿纸方便得多的情况。

由这张花里胡哨的 \(n=6\) 的图可直观感受这个构造就是正确的,而且他的最大覆盖次数是 \(2\)。
他的构造就是,横向的部分先向右边延伸 \(p_i-1\),然后再向下/上延伸 \(|p_i-i|\),最后再向右延伸 \(n-p_i\)。竖向部分同理。
现在我们有了这个构造之后,我们如何得到所有排列的情况呢?
其实很简单,我们发现了所有 \(p_i>p_{i+1}\) 的位置都是可以互换的。比如

这是因为他们都有且仅有一个交点。而且变换后该性质对于 \(p\) 依然存在(画图理解,我觉得很直观)
那么由此,我们就可以用冒泡之类的方法构造出任意排列了。时间复杂度 \(O(n^3)\),\(O(n^2)\) 在冒泡,\(O(n)\) 在维护路径变化。实现可能比较坐牢,观察一下交点位置的规律可能会好写一点。
增量法/归约法
这种方法比较类似数学中的数学归纳法。
增量法的思想是,假设已经有了一个构造方法,将它通过单次加入某元素/单元扩展到更大的情况。
归约法的思想是,找到构造目标的某个部分,将它先构造掉,然后就只需考虑删掉它之后的情况。
竞赛图哈密顿路
这是真的典问题了,以前写竞赛图专门研究过。
设已经构造出的路径为 \(p_1,p_2,...,p_{n-1}\)。
如果说 \(\exists p_{n-1}\to n\)、\(n\to p_1\),那么直接扩展我们的路径即可。
否则,一定存在一个 \(i\),使得存在 \(p_i\to n, n\to p_{i+1}\)。证明考虑此时结点 \(n\) 同时存在出边和入边,那么把边按照另外一端那个点在 \(p\) 中的顺序摆开,必然有第一条边是入边,最后一条边是出边,且中间存在前入边后出边的两种边相邻的位置,插入此处即可。
[CCO 2020] Travelling Salesperson
完全图边有红蓝色,对于每个点构造该点出发的经过所有点至少一次的路径。你需要保证路径中途只变换了一次经过边的颜色。你需要保证路径长度最小。
\(n\le 2000\)。
猜测是哈密顿路。我们现在来使用增量法加点。
设已经构造出来路径为 \(p_1,p_2,...,p_{k-1},p_k,p_{k+1},...,p_{n-1}\)。其中 \(p_{k-1}\to p_{k},p_{k}\to p_{k+1}\) 颜色分别为红、蓝。
然后你就会发现,其实就和构造哈密顿路一模一样了。
如果说端点能加,那么我必然加。否则如果说按照 \(p\) 排开有相邻边同色我也可以插入,否则那就是颜色一直红蓝红蓝交换,那么我们就可以在颜色变化处也就是 \(k\) 处插入该点。
CF1019C Sergey's problem
Sergey 五岁了!当他一岁的时候,他的父母给了他一个数;当他两岁的时候,他的父母给了他一个整数数组;三岁时他收到了一个字符串;当他四岁时,他的妈妈轻轻地叫醒他,让他做一个好孩子并给了他一棵有根树。今天是他的五岁生日!他收到了一份来自父母的有向图(没有自环)。
因为他很有好奇心,他决定找出一个集合 \(S\),使得其中的点两两之间没有连边,且对于不在集合中的任意一点 \(v\) ,都存在集合内的一点 \(u\) ,使得从 \(u\) 出发,最多经过两条边就能到达 \(v\)。输出任意一个合法的 \(S\)。
\(n,m\le 1e6\)。不保证无重边。
我们考虑最朴素的思路:每次随便选一个还在的点,然后把他的所有出点给干掉,这是一种弱化条件。显然这样子增量选择的话无法保证两两无连边。
但是这样子一选之后,我们所有选了的点,加上其指向自己一步可达的点的点边,构成一个 DAG!
现在我们尝试保证两两无边。我们提取出来选择的点构成的连通块,他必然是 DAG,那我按照拓扑排序分层选点就好了。这样子最短距离不会超过 \(1+1=2\)。
[COTS 2022] 皇后 Kraljice【未写】
有一块 \(n\times n\) 的国际象棋棋盘,依次在空格子上放置皇后,最大化放置皇后的数量。
能在一个格子上放置皇后,当且仅当这个格子被偶数个皇后攻击。
一个皇后攻击一个格子,当且仅当皇后所在的格子与被攻击格子处于同一行/列/对角线上。
\(n\le 1024\)。输出方案。
做不动了,感觉比较难,需要大量的手玩。
基础构造练习题 1【未写】
有一列实数,每次操作可以选两个实数然后把它们同时变为两数之积。
给定数列长度 \(n\),你的目标是找到一种操作方案,使得对于任意长度为 \(n\) 的数列,按照该操作方式操作之后,数列的每一项数值都相同。
\(n\le 1024\)。你最多操作 \(2047\) 次。无解输出 \(0\)。
做不动了。。。
其他类Trick
这是我补充的觉得比较有意思的做法。
生日攻击法
用于构造明显难以正向构造出的某值等于给定数的题目。
一般需要生成一些随机的样本空间,然后再从中组合出合法方案。
例题:【UR #6】智商锁
构造一张 \(\le 100\) 个结点的无向图(无重边自环)使得其生成树个数模 \(998244353\) 为 \(k\)。
\(k\in [0,998244353)\)。多组数据。
我们考虑直接生 \(1000\) 个 \(n=16\) 的连通随机图出来,然后显然由于 \(n^{n-2}>>mod\),每个图的生成树计数几乎是随机数。
那么我们考虑从中选 \(4\) 个图出来,然后使得他们的乘积为 \(\bmod\)。考虑生日攻击类似的原理,\(1000^4>>mod\),那么我们几乎必然可以找到一组合法的情况(这里我们认为随机数的乘积取模之后生成的也大概率是随机数)。当然了,我们中间需要先枚举其中两个数相乘的情况,然后用 unordered_map 存另外一半,然后直接求逆元查询即可。
Author:londono
共 5600 字。
2025-05-17 完成。
2025-05-26 整理收录。
浙公网安备 33010602011771号