构造 专题整理

 


构造

一个神秘玩意,考验人类智慧

通常会基于一些很显然,但是性质很好的事实。

下面介绍一些思维方式以及例题。

(很大部分参考了jly论文,还有自己选做的题)

一些方法与思路

1: n个数和为m, 则min<=m/n, max>=m/n

rt。如果我们需要构造一个东西,而权值的限制是n/k,那咱就搞k个,并且权值和为n。那里面肯定有一个<=n/k的。

2: 做到两件事情之一

先做一个,不行看另一个。通常这样的题目中,“做一个”是容易的,而“做不了”为我们提供了额外的条件以完成另一个任务。

另外,很多时候,我们并不需要很精确的判断其中一个操作能不能做到,可以只做其中一部分,对于不在这一部分的,有很好的性质,用来做另一个操作。

也要注意考虑两个操作之间的关系。

3: 对角线构造

对于矩形中的行列限制问题,我们可以在对角线上构造。一条不够,就把次对角线,次次对角线都带上。

题: 方法1

gym 102900B

我们考虑计算每个空格子(不是雷的格子)的权值和。

一种做法是,枚举空格子,算周围多少个雷,

一种等价做法是,枚举雷,看它被几个空格子算到(即,周围有多少空格子),

我们发现这个过程中,“雷”和“空格子”是对称的。那我们把“雷”和“空格子”互换,我们发现,权值依然相同。

dis(A,B) 表示矩形 AB 不同的数量,其中每个位置上为 1 表示雷,0 表示空格子。

A¯ 表示 A 矩阵按位取反后的结果。则有:

dis(A,B)+dis(A¯,B)=n×m (显然)

然后就好做了,因为我们发现把 B 变成 A 或者 A¯ 都是可以的,它俩加起来是 n×m,那么其中较小的一个一定 nm/2,取二者小的那个变过去就昨做完了。

loj2569

subtask 1 是 trivial 的,先找到最大最小值,每次往中间缩小,一次能确定两个值。最后确定出整个 a 只需要 (n+1)/2 步,然后直接计算即可。

我们发现这个方法不适用于 subtask 2。很明显这样 m 会到 n2

我们不妨先来一次操作把整体min,max搞出来,设为 L,R

很显然,设 di=aiai1,则 i=2ndi=ana1=RL

因此,di 中的最大值,即答案,一定超过 RLn1,设这个最小值为 lim

我们把值域区间 [L,R],分成 (RL)/lim 个块,每个块大小是 lim。对于同一块里面,显然不会存在答案,答案必须跨块。

然后我们就把每块的min,max问一遍,只会把每个数访问一次,这部分的cost是 n(RL)/lim 大约也是 n, 因此cost的和是 2n。再加上最开始问 [L,R] 那一次,正好 3n

稍微 卡一下。

代码

CF1450C2

我们知道网格图黑白染色是一个常见技巧

对于这个题,黑白染色不太够用,我们用3染色,按照 (i+j)%3 分类。设三类为 A0,A1,A2 表示 (i+j)%3=0/1/2 的那些位置集合。

我们在 A0,A1,A2 中选两个,其中一个变成 X,另一个变成 O。很明显就不会存在连续三个的相同了。

选两个,一共有三种方案。设 f(S,X/O) 表示 S 这个集合中X/O的数量。

三种方案是 (0,1),(1,2),(2,0)。我们把前者变成 X,后者变成 O。

三种方案的步数和是 f(A0,X)+f(A1,O)+f(A1,X)+f(A2,O)+f(A2,X)+f(A0,O)

对于每个集合,我们发现恰好把 f(Ai,O)f(Ai,X) 都加了一遍,这俩加起来显然超不过 Ai。则总和不超过 A0+A1+A2=nm

根据上面那个基本事实,既然三种方案的步数和是 nm,则一定有一个步数 nm/3。取那个就行了。

题: 方法2

CF1364D

我们先考虑任务2的一种情况:搞一个dfs树后,考虑每条非树边(一定是上下的边,没有横叉的)。

如果这条边连接的两个点距离 <k,那加上这条边构成的环长度就 k,然后就做完了任务2。

否则就是说,所有非树边连接的两个点距离都 k,或者没有非树边。

没有非树边可以特判一下,这是容易的

注意到,把树上奇偶深度的点分别抽出来都能构成独立集,且它俩加起来 =n,则它俩大的那个一定 >n/2

然后 kn,那么 k/2n/2。既然我们能搞出来 n/2 以上,那 k/2 就能搞了。

有非树边的话,就随便找一条,把它连接的两个点中间的那条路径一隔一的取点,就能构造出至少 k/2 个点的独立集了。

noi.ac2326

这题是比赛题,并不公开,讲下题意:

给一张图,完成两个任务之一:

  • 对这张图三染色
  • 删去一个奇环,使得图依然联通

其中一个subtask:四染色,能拿到60%的分。

考虑第二个条件的其中一种情况:随便搞一个生成树,把树边删掉,如果存在一个奇环,那么把这个奇环,至少剩下一棵树,也就是连通。判定奇环很简单,二分图染色就行。

如果不存在奇环,那相当于除去一棵树是个二分图。

注意到树也是二分图。我们把树黑白染色,把删掉树后剩下的那个二分图黑白染色,每个点有两个0/1。把它二进制压缩,就变成了一个0~3之间的数,然后就成功实现了四染色,获得60%的分。

接下来想下如何把4变成3。对树二分图染色的时候,顺便考虑一下非树边。如果当前的点被染黑了,就不去深度优先,而是广度优先,把它相邻的点都染白。

我们发现这样就不会有非树边连接两个黑点。如果它连接一黑一白,那太棒了,根本不用动。否则,它会连接两个白点。

我们把所有白点拉出来,把它们二分图染色,染成“白”和“白2”。

由于非树边没有奇环,所以我们可以做到这件事情。

然后树边连接的点显然不同色,由于“白,白2”的构造,非树边连接的点也不会同色了。

然后我们就用“黑,白,白2”三种颜色,完成了3-染色。

题: 方法3

noi.ac2333

这↑里

这里还提到了 CF1436B 这个题,和本题是类似的想法,只不过简单的多。

杂题

IOI2019 景点划分

瞎拼拼凑凑。

首先我们取 a,b,c 中最小的两个搞俩联通块就行了。因此,不妨设 abc,我们要搞 a,b

考虑DFS树。

有解当且仅当我们能删去一个 a 个点的联通块,剩下的若干联通块里面有一个 b 个点的。

注意到 ba。想要 b,怎么也得 a。删掉 a 个点,还剩 a 个点,想到用重心。

把重心,设为 rt。考虑最大的那个子树,如果它的 sizea,那显然就有解了。

如果它的 size<a,那么选一个 a 个点的联通块就一定要包含 rt,剩下的子树自然也会断开。但这不代表无解,毕竟还有非树边。

注意到非树边如果在同一个子树里,或者两端点都不在子树里,显然就没用了。

有用的边只有从 rt 上面串到 rt 下面的那些边。设 T 为不在 rt 子树中的点(即 rt “上方”的点)。如果有一条边从 T 连到了 rt 的某个子树,我们就可以选 T ,并一块选上这个子树。

S=size(T)+Txsize(x)Tx 表示 T 有一条边到 xxrt 的儿子)的子树。

如果连 S 都超不过 a,那还真就无解了。

否则就有解。为啥?

我们不断的加入 T 的每个能到的子树 x,直到 S 的值 a,就退出。由于每个子树的 size<a,这样搞一波,S 的值不会超过 2a

注意到 2a+b=a+a+ba+c+b=c。因此 S+bn,也就是剩下的点至少有 b 个,那确实是够的。构造也很好构造,就把剩下的点尽量连起来就行了。

细节有点多,小心。

代码

hdu 7066

经典构造 + smz

显然可以按二进制拆分做。需要 logn 步,然后您发现它卡你

但是 60 和 50 非常接近,所以我们只需要常数优化就能过了

接下来是一个初赛都考的技巧,The Method of Four Russians。

具体实现很简单,按16进制拆就行。

16进制下最多有16位,但是一位要用16次 “add” 运算做一下,然后复杂度就是 16×16

有这样一个做法:我们先搞出来 1...8,16,然后两步就可以搞出来一个 [1,16] 之间的数。

最开始搞需要约 16 步,然后一个数两步,就是 16+2×1650 步。

https://paste.ubuntu.com/p/zVpjJhJpFg/\

看起来这样,界卡的还挺紧的,需要忍一下。

代码

IPSC2011 I. Inverting bits

神秘人类智慧。

这题可以更加广义。如果是2次,最大能做到24。

约定几个符号:

...... 表示若干个东西的 or。

...... 表示若干个东西的 and。

考虑广义的问题:有 n 个8-bit数,要把它们取反。

每一位是独立的。我们先考虑 n 个 0/1 咋做,然后把这个做法放到每一位上就行。

对于 n 个 0/1, 假设其中有 k1。 如下是一个极其暴力,但是正确的做法:

对于一个位置 i,如果除去 i 之外还存在 k1,则 i0,取反后为 1

否则,i1,取反后为 0

注意到我们没有if语句或是计数器等东西。我们只能换个方式解决掉 k 的问题。

C(i) 表示是否存在 i1,是一个 bool 值。并且在 [0,n] 中只有一个 i 满足 C(i)=1

那么 i 位置取反后的值 (0/1)为:

j=0n C(j) & [S,|S|=j,iS,S:all 1]

S:all 1 表示 S 中全都是 1,可以用 and 判断。 可以用or解决。

注意到这玩意暴力的很,但它有一个好处:就是不用取反。

考虑如何做 C。注意到 C(i)=[ki]&[ki]k 表示实际 1 的数量。

沿用lzj的写法,令左边那个叫 mst(i),右边那个叫 lst(i)

注意到 lst(i) 就是:任选 i 个数and起来,再or起来。然后 mst(i)=∼lst(i+1)

然后可以用李子健分治!我们先把分治树建出来(就是线段树),然后对于同一层的点一块处理。

对于每一层,我们求它们的 mid

下一层里面有很多个区间,每个区间都有一个 mid。假设我们知道了这一层的 mid,我们可以先求出下一层 mid 位置的 mst 的or,这个东西可以一次取反做(取反最右边那个就行)。然后根据这一层的 mid,做一个限制:然后就得到下一层每个单点的 mid 了。

我们注意到每一层要用一次取反。所以总共就需要 log2(n+1) 上取整步。

本题:n=32 步完全可以。

AGC 30C

调整扩展法。

注意到,如果 k500,那比较容易做。有一个显然的构造是:

1 & 2 & 3 & ... & k-1 & k\\2 & 3 & 4 & ... & k & 1  \\...\\k&1 & 2 & ... & k-2 & k-1

然后你会发现,每个 1 周围都有两个 2 和两个 k,每个 2 周围都有两个 3 和两个 1,....

k>500 如何做呢?

注意到,我们把一种数 i ,奇数行的那些位置换成 i+n,依然满足条件。

然后我们就能做到 500×2=1000 了。

CF566E

瞎几把手玩,瞪规律。

N2(x) 表示 氮气 x2 邻域。 N(x) 表示 x 走一条边能到的点(注意:不包含 x 本身)

发现每个 N2 都被打乱了。但是有这样一个事实:

如果有一个 N2(x)N2(y) 包含俩元素 {u,v},则 dis(x,y)=3,且 (u,v) 一定是树上的边。

由于被打乱了,我们并不能得到距离关系。但是 (u,v) 是边,这个能得到:这与 x,y 关系不大,和集合本身的关系大。

这样做,可以得到所有非叶子节点之间的边。设非叶节点构成的树为子树 T

如果 T 只包含一个点,那肯定是菊花图。注意到菊花图的每个 N2 都是全集。所以你任意输出一个菊花图都是等价的。

如果 T 只包含两个点,也可以很容易的处理掉:随便找一个非全集的 N2,把这个集合除去中间两个点,就得到了一边的所有点,另一边就取个补集就得到了。

否则,我们可以得到哪些点是叶子,哪些点不是。

对于一个叶子节点 l ,我们只需要知道它接到了哪个非叶子节点上就行。

考虑这样做:枚举每个包含 l 的集合,and起来。可以想象,这样得到的其实是 N2(l)。原因比较简单,因为 dis2 的限制是对称的。我们枚举一个点邻域里的所有点,它们肯定都会包含这个点。

N2(l) 与非叶节点集合取交集,记为 S。设 l 接到的是 t 点,那么 S=NT(t){t}

对于每个 T 上的节点,由于 T 至少 3 个点,NT(u){u} 是两两不同的,因此就可以唯一确定接到哪里了。

uoj143

结论:二进制反转(reverse)之后等差子序列个数最小。

首先,长度为 2 的等差数列不可避。其次,如果两个数相同,怎么也会有等差数列(由此,我们可以先假设数两两不同)。

接下来,我们可以构造,使得不存在长度为 3 的等差数列!

有个显然的东西:如果一个序列里不存在长度为 3 的等差数列,那么我们给它删掉几个数,那还是不存在。

因此我们考虑把值变成连续的,从 0 开始。

先玩几个小数据:0,1,2,3

我们把它排成 0,2,1,3,它就没了。

对于 0,1,2,3,4,5,6,7,我们把它按FFT的方式洗牌(奇偶分组,递归操作)(其实就是二进制reverse)之后,我们发现它也没等差数列了!!好对啊,为什么?

考虑一次奇偶分组之后,就不存在奇数公差的等差了。即,公差都是偶数。因为它一定会形如“左半 - 右半 - 左半 ...”,要 交替跨区,不可能递增。

分组之后,我们对两边递归操作。我们发现公差都变成了 4 的倍数。这其实也可以递归证明:奇偶分组后,两边差分都是 2 的倍数。我们给它除一个 2 后,再分组,公差必须是 2 的倍数。再把 2 乘回来,公差就得是 4 的倍数。

做一次,公差是 2 倍数。做两次,公差是 4 倍数... 做 k 次,公差是 2k 的倍数。

我们取一个足够长的序列:0,1,2...2631。对它做 63 次洗牌,那公差就得是 263 的倍数:显然不可能存在。因此不存在长度为 3 的等差数列。

我们固然不用搞出来这个序列。注意到它其实就是二进制reverse,直接按这个东西重新排序一下就行。代码极短。

后记

与nealchen的交流

和nealchen交流了一下,他认为,做构造题要学会

  • 丢条件:要构造一个 P 里面的元素,我们构造一个 QP 集合里的元素。
  • 联想:对于集合 P 的条件,找一个和它性质很像的东西 P 来考虑。

还有就是多做题(

一些总结

感觉这玩意就是凑吧,观察一下给定的条件有啥性质,做一些常见变形,猜猜结论,然后对着它做就行了。

对于条件很多的题,可以一个条件一个条件的做。

怎么只有这么点例题啊

会整的会整的

posted @   Flandre-Zhu  阅读(154)  评论(0编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
点击右上角即可分享
微信分享提示