构造 专题整理
构造
一个神秘玩意,考验人类智慧
通常会基于一些很显然,但是性质很好的事实。
下面介绍一些思维方式以及例题。
(很大部分参考了jly论文,还有自己选做的题)
一些方法与思路
1: n个数和为m, 则min<=m/n, max>=m/n
rt。如果我们需要构造一个东西,而权值的限制是n/k,那咱就搞k个,并且权值和为n。那里面肯定有一个<=n/k的。
2: 做到两件事情之一
先做一个,不行看另一个。通常这样的题目中,“做一个”是容易的,而“做不了”为我们提供了额外的条件以完成另一个任务。
另外,很多时候,我们并不需要很精确的判断其中一个操作能不能做到,可以只做其中一部分,对于不在这一部分的,有很好的性质,用来做另一个操作。
也要注意考虑两个操作之间的关系。
3: 对角线构造
对于矩形中的行列限制问题,我们可以在对角线上构造。一条不够,就把次对角线,次次对角线都带上。
题: 方法1
gym 102900B
我们考虑计算每个空格子(不是雷的格子)的权值和。
一种做法是,枚举空格子,算周围多少个雷,
一种等价做法是,枚举雷,看它被几个空格子算到(即,周围有多少空格子),
我们发现这个过程中,“雷”和“空格子”是对称的。那我们把“雷”和“空格子”互换,我们发现,权值依然相同。
设 表示矩形 和 不同的数量,其中每个位置上为 表示雷, 表示空格子。
设 表示 矩阵按位取反后的结果。则有:
(显然)
然后就好做了,因为我们发现把 变成 或者 都是可以的,它俩加起来是 ,那么其中较小的一个一定 ,取二者小的那个变过去就昨做完了。
loj2569
subtask 1 是 trivial 的,先找到最大最小值,每次往中间缩小,一次能确定两个值。最后确定出整个 只需要 步,然后直接计算即可。
我们发现这个方法不适用于 subtask 2。很明显这样 会到 。
我们不妨先来一次操作把整体min,max搞出来,设为 。
很显然,设 ,则
因此, 中的最大值,即答案,一定超过 ,设这个最小值为 。
我们把值域区间 ,分成 个块,每个块大小是 。对于同一块里面,显然不会存在答案,答案必须跨块。
然后我们就把每块的min,max问一遍,只会把每个数访问一次,这部分的cost是 。 大约也是 , 因此cost的和是 。再加上最开始问 那一次,正好 。
要 稍微 卡一下。
CF1450C2
我们知道网格图黑白染色是一个常见技巧
对于这个题,黑白染色不太够用,我们用3染色,按照 分类。设三类为 表示 的那些位置集合。
我们在 中选两个,其中一个变成 X,另一个变成 O。很明显就不会存在连续三个的相同了。
选两个,一共有三种方案。设 表示 这个集合中X/O的数量。
三种方案是 。我们把前者变成 X,后者变成 O。
三种方案的步数和是
对于每个集合,我们发现恰好把 和 都加了一遍,这俩加起来显然超不过 。则总和不超过 。
根据上面那个基本事实,既然三种方案的步数和是 ,则一定有一个步数 。取那个就行了。
题: 方法2
CF1364D
我们先考虑任务2的一种情况:搞一个dfs树后,考虑每条非树边(一定是上下的边,没有横叉的)。
如果这条边连接的两个点距离 ,那加上这条边构成的环长度就 ,然后就做完了任务2。
否则就是说,所有非树边连接的两个点距离都 ,或者没有非树边。
没有非树边可以特判一下,这是容易的
注意到,把树上奇偶深度的点分别抽出来都能构成独立集,且它俩加起来 ,则它俩大的那个一定 。
然后 ,那么 。既然我们能搞出来 以上,那 就能搞了。
有非树边的话,就随便找一条,把它连接的两个点中间的那条路径一隔一的取点,就能构造出至少 个点的独立集了。
noi.ac2326
这题是比赛题,并不公开,讲下题意:
给一张图,完成两个任务之一:
- 对这张图三染色
- 删去一个奇环,使得图依然联通
其中一个subtask:四染色,能拿到60%的分。
考虑第二个条件的其中一种情况:随便搞一个生成树,把树边删掉,如果存在一个奇环,那么把这个奇环,至少剩下一棵树,也就是连通。判定奇环很简单,二分图染色就行。
如果不存在奇环,那相当于除去一棵树是个二分图。
注意到树也是二分图。我们把树黑白染色,把删掉树后剩下的那个二分图黑白染色,每个点有两个0/1。把它二进制压缩,就变成了一个0~3之间的数,然后就成功实现了四染色,获得60%的分。
接下来想下如何把4变成3。对树二分图染色的时候,顺便考虑一下非树边。如果当前的点被染黑了,就不去深度优先,而是广度优先,把它相邻的点都染白。
我们发现这样就不会有非树边连接两个黑点。如果它连接一黑一白,那太棒了,根本不用动。否则,它会连接两个白点。
我们把所有白点拉出来,把它们二分图染色,染成“白”和“白2”。
由于非树边没有奇环,所以我们可以做到这件事情。
然后树边连接的点显然不同色,由于“白,白2”的构造,非树边连接的点也不会同色了。
然后我们就用“黑,白,白2”三种颜色,完成了3-染色。
题: 方法3
noi.ac2333
见 这↑里
这里还提到了 CF1436B 这个题,和本题是类似的想法,只不过简单的多。
杂题
IOI2019 景点划分
瞎拼拼凑凑。
首先我们取 中最小的两个搞俩联通块就行了。因此,不妨设 ,我们要搞 。
考虑DFS树。
有解当且仅当我们能删去一个 个点的联通块,剩下的若干联通块里面有一个 个点的。
注意到 。想要 ,怎么也得 。删掉 个点,还剩 个点,想到用重心。
把重心,设为 。考虑最大的那个子树,如果它的 ,那显然就有解了。
如果它的 ,那么选一个 个点的联通块就一定要包含 ,剩下的子树自然也会断开。但这不代表无解,毕竟还有非树边。
注意到非树边如果在同一个子树里,或者两端点都不在子树里,显然就没用了。
有用的边只有从 上面串到 下面的那些边。设 为不在 子树中的点(即 “上方”的点)。如果有一条边从 连到了 的某个子树,我们就可以选 ,并一块选上这个子树。
记 , 表示 有一条边到 ( 为 的儿子)的子树。
如果连 都超不过 ,那还真就无解了。
否则就有解。为啥?
我们不断的加入 的每个能到的子树 ,直到 的值 ,就退出。由于每个子树的 都 ,这样搞一波, 的值不会超过 。
注意到 。因此 ,也就是剩下的点至少有 个,那确实是够的。构造也很好构造,就把剩下的点尽量连起来就行了。
细节有点多,小心。
hdu 7066
经典构造 + smz
显然可以按二进制拆分做。需要 步,然后您发现它卡你
但是 60 和 50 非常接近,所以我们只需要常数优化就能过了
接下来是一个初赛都考的技巧,The Method of Four Russians。
具体实现很简单,按16进制拆就行。
16进制下最多有16位,但是一位要用16次 “add” 运算做一下,然后复杂度就是
有这样一个做法:我们先搞出来 ,然后两步就可以搞出来一个 之间的数。
最开始搞需要约 步,然后一个数两步,就是 步。
https://paste.ubuntu.com/p/zVpjJhJpFg/\
看起来这样,界卡的还挺紧的,需要忍一下。
IPSC2011 I. Inverting bits
神秘人类智慧。
这题可以更加广义。如果是2次,最大能做到24。
约定几个符号:
表示若干个东西的 or。
表示若干个东西的 and。
考虑广义的问题:有 个8-bit数,要把它们取反。
每一位是独立的。我们先考虑 个 0/1 咋做,然后把这个做法放到每一位上就行。
对于 个 0/1, 假设其中有 个 。 如下是一个极其暴力,但是正确的做法:
对于一个位置 ,如果除去 之外还存在 个 ,则 为 ,取反后为 。
否则, 为 ,取反后为 。
注意到我们没有if语句或是计数器等东西。我们只能换个方式解决掉 的问题。
设 表示是否存在 个 ,是一个 bool 值。并且在 中只有一个 满足 。
那么 位置取反后的值 (0/1)为:
表示 中全都是 ,可以用 and 判断。 可以用or解决。
注意到这玩意暴力的很,但它有一个好处:就是不用取反。
考虑如何做 。注意到 , 表示实际 的数量。
沿用lzj的写法,令左边那个叫 ,右边那个叫 。
注意到 就是:任选 个数and起来,再or起来。然后 。
然后可以用李子健分治!我们先把分治树建出来(就是线段树),然后对于同一层的点一块处理。
对于每一层,我们求它们的 。
下一层里面有很多个区间,每个区间都有一个 。假设我们知道了这一层的 ,我们可以先求出下一层 位置的 的or,这个东西可以一次取反做(取反最右边那个就行)。然后根据这一层的 ,做一个限制:然后就得到下一层每个单点的 了。
我们注意到每一层要用一次取反。所以总共就需要 上取整步。
本题:, 步完全可以。
AGC 30C
调整扩展法。
注意到,如果 ,那比较容易做。有一个显然的构造是:
然后你会发现,每个 周围都有两个 和两个 ,每个 周围都有两个 和两个 ,....
那 如何做呢?
注意到,我们把一种数 ,奇数行的那些位置换成 ,依然满足条件。
然后我们就能做到 了。
CF566E
瞎几把手玩,瞪规律。
记 表示 氮气 的 邻域。 表示 走一条边能到的点(注意:不包含 本身)
发现每个 都被打乱了。但是有这样一个事实:
如果有一个 包含俩元素 ,则 ,且 一定是树上的边。
由于被打乱了,我们并不能得到距离关系。但是 是边,这个能得到:这与 关系不大,和集合本身的关系大。
这样做,可以得到所有非叶子节点之间的边。设非叶节点构成的树为子树 。
如果 只包含一个点,那肯定是菊花图。注意到菊花图的每个 都是全集。所以你任意输出一个菊花图都是等价的。
如果 只包含两个点,也可以很容易的处理掉:随便找一个非全集的 ,把这个集合除去中间两个点,就得到了一边的所有点,另一边就取个补集就得到了。
否则,我们可以得到哪些点是叶子,哪些点不是。
对于一个叶子节点 ,我们只需要知道它接到了哪个非叶子节点上就行。
考虑这样做:枚举每个包含 的集合,and起来。可以想象,这样得到的其实是 。原因比较简单,因为 的限制是对称的。我们枚举一个点邻域里的所有点,它们肯定都会包含这个点。
把 与非叶节点集合取交集,记为 。设 接到的是 点,那么
对于每个 上的节点,由于 至少 个点, 是两两不同的,因此就可以唯一确定接到哪里了。
uoj143
结论:二进制反转(reverse)之后等差子序列个数最小。
首先,长度为 的等差数列不可避。其次,如果两个数相同,怎么也会有等差数列(由此,我们可以先假设数两两不同)。
接下来,我们可以构造,使得不存在长度为 的等差数列!
有个显然的东西:如果一个序列里不存在长度为 的等差数列,那么我们给它删掉几个数,那还是不存在。
因此我们考虑把值变成连续的,从 开始。
先玩几个小数据:
我们把它排成 ,它就没了。
对于 ,我们把它按FFT的方式洗牌(奇偶分组,递归操作)(其实就是二进制reverse)之后,我们发现它也没等差数列了!!好对啊,为什么?
考虑一次奇偶分组之后,就不存在奇数公差的等差了。即,公差都是偶数。因为它一定会形如“左半 - 右半 - 左半 ...”,要 交替跨区,不可能递增。
分组之后,我们对两边递归操作。我们发现公差都变成了 的倍数。这其实也可以递归证明:奇偶分组后,两边差分都是 的倍数。我们给它除一个 后,再分组,公差必须是 的倍数。再把 乘回来,公差就得是 的倍数。
做一次,公差是 倍数。做两次,公差是 倍数... 做 次,公差是 的倍数。
我们取一个足够长的序列:。对它做 次洗牌,那公差就得是 的倍数:显然不可能存在。因此不存在长度为 的等差数列。
我们固然不用搞出来这个序列。注意到它其实就是二进制reverse,直接按这个东西重新排序一下就行。代码极短。
后记
与nealchen的交流
和nealchen交流了一下,他认为,做构造题要学会
- 丢条件:要构造一个 里面的元素,我们构造一个 集合里的元素。
- 联想:对于集合 的条件,找一个和它性质很像的东西 来考虑。
还有就是多做题(
一些总结
感觉这玩意就是凑吧,观察一下给定的条件有啥性质,做一些常见变形,猜猜结论,然后对着它做就行了。
对于条件很多的题,可以一个条件一个条件的做。
怎么只有这么点例题啊
会整的会整的
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!