构造 专题整理

构造

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

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

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

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

一些方法与思路

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

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

2: 做到两件事情之一

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

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

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

3: 对角线构造

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

题: 方法1

gym 102900B

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

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

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

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

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

(overline{A}) 表示 (A) 矩阵按位取反后的结果。则有:

(dis(A,B)+dis(overline{A},B)=n imes m) (显然)

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

loj2569

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

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

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

很显然,设 (d_i=a_i-a_{i-1}),则 (sumlimits_{i=2}^{n}d_i=a_n-a_1=R-L)

因此,(d_i) 中的最大值,即答案,一定超过 (lceil dfrac{R-L}{n-1} ceil),设这个最小值为 (lim)

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

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

稍微 卡一下。

代码

CF1450C2

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

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

我们在 (A_0,A_1,A_2) 中选两个,其中一个变成 X,另一个变成 O。很明显就不会存在连续三个的相同了。

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

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

三种方案的步数和是 (f(A_0,X)+f(A_1,O)+f(A_1,X)+f(A_2,O)+f(A_2,X)+f(A_0,O))

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

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

题: 方法2

CF1364D

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

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

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

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

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

然后 (kle n),那么 (k/2le n/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) 中最小的两个搞俩联通块就行了。因此,不妨设 (ale ble c),我们要搞 (a,b)

考虑DFS树。

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

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

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

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

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

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

(S=size(T)+sumlimits_{T o x} size(x))(T o x) 表示 (T) 有一条边到 (x)(x)(rt) 的儿子)的子树。

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

否则就有解。为啥?

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

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

细节有点多,小心。

代码

hdu 7066

经典构造 + smz

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

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

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

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

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

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

最开始搞需要约 (16) 步,然后一个数两步,就是 (16+2 imes 16le 50) 步。

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

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

代码

IPSC2011 I. Inverting bits

神秘人类智慧。

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

约定几个符号:

(igcuplimits_{...} ...) 表示若干个东西的 or。

(igcaplimits_{...} ...) 表示若干个东西的 and。

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

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

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

对于一个位置 (i),如果除去 (i) 之外还存在 (k)(1),则 (i)(0),取反后为 (1)

否则,(i)(1),取反后为 (0)

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

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

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

[igcuplimits_{j=0}^{n} C(j) & [exist S, |S|=j,i otin S, S: all 1] ]

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

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

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

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

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

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

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

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

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

本题:(n=3)(2) 步完全可以。

AGC 30C

调整扩展法。

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

[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 imes 2=1000) 了。

CF566E

瞎几把手玩,瞪规律。

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

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

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

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

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

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

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

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

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

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

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

对于每个 (T') 上的节点,由于 (T') 至少 (3) 个点,(N_{T'}(u)cup {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) 次,公差是 (2^k) 的倍数。

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

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

后记

与nealchen的交流

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

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

还有就是多做题(

一些总结

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

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

怎么只有这么点例题啊

会整的会整的

原文地址:https://www.cnblogs.com/LightningUZ/p/15321778.html