[正睿集训2021] 构造专练

没想到还有构造题专练这个玩意,\(\tt noip2020\) 考了之后都重视起来了吧。

例一

题目描述

是否存在 \(3\) 个长度为 \(n\)\([0,n)\) 的排列 \(a,b,c\) ,使得 \(a_i+b_i=c_i\mod n\)

不是输出 jzm yyds!,是的话输出构造方案。

构造方法

我一开始想的是分奇偶讨论,\(n\) 是奇数的话就会很好构造,我们让 \(a,b\) 都成为 \(0,1,2,3...\) 的数列即可,那么 \(c\) 也一定是一个排列(先取遍偶数,在超过 \(n\) 是从 \(1\) 开始取遍奇数)

偶数我不知道如何构造,但可以证明他是无解的,因为 \(a,b\) 的数之和是 \((n-1)n=0\mod n\),但是 \(c\) 的数之和却是 \(\frac{(n-1)n}{2}=\frac{n}{2}\mod n\),两边总和都不相等怎么构造?

普普通通的构造题,我觉得难点应该是证明无解。

例二

题目描述

\(2^n-1\) 个点的完全图,你需要找出尽量多边互不相交的三元环,输出最优情况下的方案。

\(n\leq 10\)

构造方法

不要被 最优方案 这几个字迷惑了,从构造的角度来思考:如果能构造出答案达到上界,那么他一定是最优解

先把答案上界算出来,也就是:\(\frac{(2^n-1)(2^n-2)}{6}\),因为任意连续的三个正整数一定有 \(3\) 的倍数,所以答案上界是整数,那么我们现在想让每条边用且仅有一次,答案上界其实是构造方法的提示

我们把点从 \([1,2^n-1]\) 编号,把所有满足 \(x\oplus y\oplus z=0\)\((x,y,z)\) 取出当作三元环,\(x,y,z\) 应互不相同,因为 \(x,y\) 确定之后 \(z\) 也确定了,所以方案数是 \((2^n-1)(2^n-2)\),由于每个三元环会被统计 \(6\) 次,这种构造能取到上界,而且每条边 \((x,y)\) 都只被访问了一次。

例三

题目描述

\(2n\) 个点的完全图,要把这些点分成 \(2n-1\) 组,每组 \(n\) 条边,且每组都是一个匹配(任意两条边没有公共点)

\(n\leq 1000\)

构造方法

我觉得这道题挺离谱的,我就算知道了做法也不知道是怎么想出来的。

构造 \(n\) 组吧,那这 \(n\) 组肯定不是乱来的,每组都要有一个特征,我们让第 \(i\) 组的特征为 \(2k=i\mod (2n-1)\),然后让 \(k\)\(2n-1\) 连边。对于 \(x+y=i\mod 2n-1\)\((x,y)\) 都归到这一组里面,画个图体会一下:

在这里插入图片描述

因为 \(2n-1\)被擦去的点 ,所以往两边扩展时是不用管它的,而且扩展步数两边都为 \(1\) ,有点对称的感觉了。

例四

题目描述

\(n\) 个点的完全图,从中选出尽量多互不相交的树,输出方案。

\(n\leq 1000\)

构造方法

又要去构造上界了,\(\frac{n(n-1)}{2(n-1)}=\frac{n}{2}\),所以 \(n\) 为偶数的时候我们想要把边用完,\(n\) 为奇数的时候会剩下一些边。

这时候要引入一种神奇的构造方法:归纳构造法。也就是我们先假设 \(2k\) 个点的最优方案是构造出来的了,我们把它推到 \(2k+1\)\(2k+2\) 的情况之中,类似于数学归纳法。

对于 \(2k+1\):我们把 \(2k+1\)\([1,k]\) 中代表 \(k\) 棵树的点连边,那么还是 \(k\) 棵树,剩下没用的边我们不要了。

对于 \(2k+2\):我们把 \(2k+1\)\([1,k]\) 中的点连边,\(2k+2\)\([k+1,2k]\) 中的点连边,这样就继承了 \(k\) 棵树。然后我们把 \((2k+1,2k+2)\) 连边,再把 \(2k+1\) 连向所有 \(i\in[k+1,2k]\)\(2k+2\) 连向所有 \(j\in[1,k]\) 。这样就用完了所有的边并且得到了 \(k+1\) 棵树。

例五

题目描述

平面上有 \(n\) 个蓝色的点,你需要加上 \(k\) 个红色的点,是的任意三个蓝点组成的三角形内部都必须至少有一个红点。注意该红点必须在三角形内部,而不能在边上,要最小化 \(k\) 的大小。

\(n\leq 100\)

构造方法

构造最小化问题一定要求答案的下界,但是这道题答案的下界不是很好求。

其实我们想求的是划分出来最多有多少个互不相交的三角形,那么可以先把凸包找出来。也就是我们先找出 \(n\) 个点的凸包 \(l_1\) ,再找出剩下点的凸包 \(l_2 ...\) 以此类推,每个点会恰好被包含在一个凸包中

找出来的凸包有什么性质呢?外层凸包一定是把内层凸包完全包进去了,所以我们__求出相邻相邻两层三角剖分的数量相加就得到了答案的下界__,可以把凸包等价看成一个圆形,如下图:

在这里插入图片描述

所以三角剖分的数量是 \(l_i+l_{i+1}\),最里面要单独考虑,是 \(l_m-2\) ,那么把全部相加:\((l_1+l_2)+(l_2+l_3)....+l_m-2=2n-2-l_1\),因为除了 \(l_1\) 之外都出现了两次。

那么怎么构造出这个下界呢?我们 随机 一个角度 \(p\) ,把 \(p\)\(-p\) 的极小量放在点的两边,由于三角形内角和 \(180\),而我们安放的角度可以看成直线,根据鸽巢原理,\(p,-p\) 一定有一个放在了三角形的内部,现在我们所用的点数是 \(2n\)

但好像还是有点多了,对于最外层的凸包是可以单独考虑的,首先每个点都只需要 \(p\) 或者 \(-p\) ,那么可以少放 \(l_1\) 个点,然后凸包最外面的两个点可以 \(p\)\(-p\) 都不放,因为外层凸包其实是从 \(x_1\)(第一个点)斜率单减到 \(x_n\),再从 \(x_n\) 斜率单减到 \(x_1\) 的闭合图形,上下两部分每一部分都会有这样一个 \(p\)\(-p\) 都在外面的点。那么就只需要 \(2n-2-l_i\) 个红点了。

现在回来解释为什么 \(p\) 要求随机,因为不能和边重合,多随机几次就可以满足这个条件了。

例六

题目描述

有一个 \(n\times n\) 的方格表,每个格子里有字母。每次可以把某一行的所有字母向右循环平移若干格,或者把某列字母向下平移若干格。如果某一行出现了连续的三个字母 key 则称为一个键。要求在 \(10000\) 次操作内最大化键的数量。

\(n\leq 40\)

构造方法

先考虑最优解长什么样子,其实就是每一行都排着 keykey...

我们考虑一列一列的填,假设之前已经填好了 key ,我们要把这一列填上 k ,先不管有没有这么多的 k 。我们要实现的操作是找到某一个 \(i\) 列不是 \(k\) 的第 \(j\) 行 ,随便找到右边的一个 k 移到 \(j\) 行下一行,把 \((j,i)\) 的那个字符移到 \((j,?)\) 再用下面的 k 把他换掉,移回去即可。原来的字符不需要关心他去哪了所以这个方法是对的。

如果这一列 k 填不满呢?可以先把没有用的字符放进来,否则把没有用的 e,k 放进来。

列数不为 \(3\) 的倍数,后面的若干列可以当作工具人。但是如果列数为 \(3\) 的倍数最后一列就没法换了,所以这种情况还要讨论。太复杂了我不想写了,直接贴 \(\tt ppt\),但思路还是跟上面挺像的,找到垃圾字符来搞

在这里插入图片描述

例七

题目描述

给定一个 \([1,n^2+n]\) 的排列,你需要从中选出一个长度为 \(2n\) 的子序列使得第 \(2k\) 和第 \(2k-1\) 大的数相等。如果可以的话请输出这个子序列,否则输出不可能。

\(n\leq 1000\)

构造方法

一定要对一些数据敏感,比如 \(n^2+n=n(n+1)\),那么可以把原序列划分成 \(n+1\) 段,每一段选两个数出来就正好可以凑够 \(2n\) 个数,那我们是否一定能构造出来呢?

我们按权值从小到大构造,找到当前次小值最小的段,然后取出它的最小值和次小值加入子序列中。这个操作会导致其他段中的一些数失效,也就是比这个次小值小的我们就不用了。但是每个段最多只会失效 \(1\) 个,就算 \(n-1\) 次失效满它还剩下 \(2\) 个。这样选完我们把这个段删掉就可以了。

例八

题目描述

有一个 \(2n\times m\) 的棋盘,有 \(n\times m\) 个红格子和 \(n\times m\) 个蓝格子。保证棋盘的左上角是红色,右下角是蓝色,你需要把红色节点之间两两中心连一个向量(相当于给无向边定方向),把蓝色节点两两之间中心连一个向量。要求这些向量求和的结果是 \(0\) ,让你构造出一种方案。

构造方法

如果我们从图论的角度思考,那么 \(nm\) 为奇数的情况是可以做的,因为这时候每个点的度数都是偶数,那么所有红点\(/\)蓝点可以构成欧拉回路,由于是闭合回路那么向量之和一定是 \(0\)

如果 \(nm\) 是偶数就不能欧拉回路了。这时候我们就要利用题目里看起来很奇怪的条件:保证... 如果我们先把这两个点遮住不看那么剩下的点就是奇数个,可以用欧拉回路的方法来做了。

现在考虑加入这两个点,我们可以把所有向量归到对角线的这个位置然后抵消。首先我们确定一个对称中心(这个点一定不是整点),那么所有点都是两两对称的。如果这两个对称点是不同颜色的,那么红色连左上角,蓝色连右下角,这两个向量由于对称的原因就可以消掉了。

如果这两个对称点是不同颜色的,那么如果红色都连左上角,如果蓝色都连右下角,那么连出来的向量长度都是对角线但是方向不同。容易发现不同方向的向量数量是一样的,这种情况的向量也可以抵消。

例九

题目描述

给定一个环,环上每个点是三种颜色(\(\tt RGB\))之一,若一个点左右两边点的颜色不同,则你可以改变这个点的颜色。问能否在 \(10n\) 的操作之内,把初始环变成目标环,如果有的话输出方案。

\(5\leq n\leq 10^5\)

构造方法

首先我们知道 转化是可逆的 ,你怎么来的还是能够怎么回去。所以我们可以找到一个中间状态,让初始环可以转化到它,目标环也可以转化到他(类似于双向 \(\tt bfs\)),那么中间状态的选择就很重要。我们选择一个最宽松的中间状态:任意间隔为 \(2\) 的点颜色都不相同

先来解决这个问题:初始环转化到的 初始(中间)状态 怎么转化到目标环的 目标(中间)状态 ?就从 \(1\) 开始变化呗,我们把 \(1\) 变成目标颜色,但是这可能会导致一些问题,比如 \(3\) 或者 \(n-1\) 和它的颜色相同,这样肯定是不合法的,因为我们要保证任意时刻都是可以变化的

那么我们可以把 \(3,n-1\) 肯定是可以变化的,我们把它们变成三种颜色的剩下两种(还要讨论一下它们相邻的颜色才能做决断哦),那么可以用这种方法一直推下去,\(i\) 就只用变一下 \(i+2\) 就行了。

第二个问题:初始化怎么转化到初始中间状态(目标环同理)?我们先找到一个可以变化的位置 \(i\)(全部颜色相同的情况需要特判一下),此时 \(i-1,i\) 颜色是不相同的,我们把 \(i\) 变成和 \(i+2\) 颜色不相同的,那么 \(i+1\) 位置就解锁了,我们把它变成和 \(i+3\) 颜色不相同的 \(....\) 以此类推,最后所有的位置都会被 解锁

最后你会发现 \(10n\) 次操作是绰绰有余的。

例十

题目描述

有一颗二叉树,有 \(n\) 个叶节点,它们的权值未知但是只可能是 \(0/1\) 。每个点的权值为两个子节点的与非值(即先做 \(\tt and\) 运算再取反),初始权值不定,你需要钦定尽量多的叶节点使其满足下列条件:

  • 没有被钦定的叶节点全部取 \(1\) 时,根节点的值和所有叶节点全取 \(1\) 的值相等。
  • 没有被钦定的叶节点全部取 \(0\) 时,根节点的值和所有叶节点全取 \(0\) 的值相等。

\(n\leq 10^5\)

构造方法

直接构造最优解,首先钦定 \(n\) 个点的情况可以特判掉,就是全部取 \(0/1\) 值相等的时候是可以把所有的叶节点都钦定的,但是这种情况并不是普遍的。

考虑构造 \(n-1\) 个点被钦定的情况,我们现在可以用的条件是全部取 \(0\) 和全部取 \(1\) 的值是不相等的,那么当那个未被钦定的点取 \(0\) 的时候是和全部取 \(0\) 的情况相等的,取 \(1\) 的时候是和全部取 \(1\) 的情况相等的,我们考虑下面的几种钦定序列(假设有 \(5\) 个叶节点,全部取 \(0\) 值为 \(0\) ,全部取 \(1\) 值为 \(1\)):

\[00000 \]

\[10000 \]

\[11000 \]

\[11100 \]

\[11110 \]

\[11111 \]

为什么我要罗列这些钦定情况呢?因为设 \(f(x)\) 为前缀 \(x\)\(1\) 的,后缀 \(n-x\)\(0\) 钦定的函数值,那么由于总的变化是 \(0->1\),中间一定有 \(f(x)->f(x+1)\)\(0->1\) ,那么就和我们想要的正好吻合。我们就可以得到这样的钦定序列:$$11...x00...$$

但是还没完,暴力查找上述的 \(x\) 时间复杂度是 \(O(n^2)\) 的,其实可以用二分的方法优化,类似于二分法查非单调函数的 \(0\) 点,如果端点值异号,那么就可以往某个半边分治。这道题我们二分到一个 \(x\) 时可能出现 \(4\) 种取值,但是对于每一种我们都可以确定答案,所以时间复杂度 \(O(n\log n)\)

这道题最迷惑的地方在于 与非运算 ,其实把他换成任意一个位运算都是可以的。

例十一

题目描述

一个 \(n\) 个点的简单无向图(无重边无自环,但是不保证联通),你可以询问若干次,每次询问一颗 \(n\) 个点的树,交互库会返回这棵树里有多少边和无向图中的边重合。请还原这个无向图。

\(n\leq 500,m\leq2000\),询问次数 \(20000\)

构造方法

我一开始有一个思路,暴力判断每条边 \((i,j)\) 是否存在,就是询问保留这条边的生成树,再问把这条边换成另一条边的生成树,然后分类讨论一下,但是询问次数达到了 \(25000\) 而且难以卡下来。

需要换一种复杂度(这里就称询问次数为算法的复杂度吧),我们期望的复杂度应该是 \(O(m\log n)\) 的,因为这道题的 \(m\) 比较小(并且 \(n\) 可以开到更大那暴力算法就完全 \(gg\) 了)

我们检验每个点的连出去的边,如果不要求是生成树的话那其实挺好做的,我们可以用二分的思路,先问左半边是否有边,如果有的话就继续做左半边;如果右半边有边的话就做右半边,不难发现复杂度是 \(O(m\log n)\) 的。

但是询问要求是生成树啊,那说明有一些边会干扰我们的判断,那么我们就把这些边设置为废边,也就是我们预先就知道这些边是否存在。我们可以把 \(1-n\) 的环拿出来做一遍,断掉 \((i,i+1)\) 后得到的答案相加除以 \(n-1\) 就是总边数,得到总边数后可以对应的去看 \((i,i+1)\) 是否存在。

然后把底层的点连起来,这些边是知道的,可以看下图:

在这里插入图片描述

例十二

题目描述

有一个 \(n\times m\) 的矩阵,你可以执行以下三种操作若干次,使得整个矩阵的元素都变为 \(0\)

  • 把某一行里的元素全部加上任意整数 \(k\)
  • 把某一列里的元素全部加上任意整数 \(k\)
  • 把某一主对角线里面的元素全部加上任意整数 \(k\)

主对角线指行编号和列编号之差为定值的一些格子,你可以执行总共 \(6000\) 次操作或输出无解。

\(2\leq n,m\leq 1000\)

构造方法

一般这个加减的题你要考虑什么东西是定值,这道题有点奇怪,对于任意一个 \(3\times 3\) 的矩阵,把正号上的值乘上 \(1\) 加入权值,把负号上的值乘上 \(-1\) 加入权值,则权值不改变,如下图:

在这里插入图片描述

如果我们要全部数都变成 \(0\) ,那么这个权值一定是 \(0\) 。因为权值不变,所以__最开始的时候权值为 \(0\) 是有解的必要条件__

考虑权值怎么用,如果我们得到了其中 \(5\) 个位置的值都是 \(0\) ,那么剩下的一个位置也一定是 \(0\) 。考虑把前两行和前两列都消成 \(0\) ,如果我们做到了,那么用第三行的第一个矩阵可以推出剩下的一个位置是 \(0\) 。然后第三行一直推到底,然后再进行下一行,就推出了这时候所有位置的权值都是 \(0\)

现在的问题变成了是否能把前两行和前两列变成 \(0\) ,我给出构造,请看图:

在这里插入图片描述

上述过程是我从右到左,先通过操作列把上面那个变成 \(0\) ,然后通过操作主对角线把下面那个也变成 \(0\) ,以此类推,最后我们能把前两行变成 \(0\) ,至于这些操作怎么影响了其他的数我们不用关心。

在这里插入图片描述

上述过程是从上到下,先通过操作行把右边那个变成 \(0\) ,再通过操作主对角线把左边那个也变成 \(0\) ,以此类推,最后左下角可以直接操作对角线,那么我们一定能够把前两行和前两列消成 \(0\)

现在你发现了吧,权值为 \(0\) 就是有解的充要条件

例十三

题目描述

有一个排列 \(a_1....a_n\)\(n\) 个集合初始分别为 \(\{a_1\}...\{a_n\}\) ,每次可以合并两个集合得到新的集合,原来的两个集合依然保留。条件是其中一个集合的最大值大于另一个集合的最小值(即两者值域区间不交)。

给定 \(q\)\(l_i,r_i\) ,你需要在 \(2.2e6\) 次操作以内合并出一些集合,使得每组询问的集合 \(\{a_{l_i}....a_{r_i}\}\) 都已经被合并出来过了(不能有其他的东西)

\(n\leq 2^{12},Q\leq 2^{16}\)

构造方法

最重要的就是观察操作次数,本题的操作次数比 \(2^{21}\) 要略大一些。

现在就要凑出最可能的复杂度,\(O(q\log n)\) 之类的差的有点远,\(O(q\sqrt n)\) 偏大了难以卡,\(O(n\sqrt q)\) 到可以一试,因为这是分块的复杂度,而分块会带有一个 \(2\) 的常数,正好符合 \(2^{21}\) 这个数值。

那就要考虑对什么东西分块,你发现本题难做的原因是 一个集合的最大值大于另一个集合的最小值 ,关键在值域。所以我们按值域分块,具体来说,我们把值域划分为 \(\frac{n}{B}\) 个长度为 \(B\) 的值域区间,每个区间内按下标放权值处于该值域的 \(a_i\),这样我们的答案就可以取出每个块在 \([l,r]\) 的子区间,然后可以直接把这些子区间拼起来,回答询问的复杂度是 \(O(\frac{n}{B}\times q)\)

考虑预处理每个块的区间集合,我们期望的复杂度是 \(O(nB)\) 的,每个区间处理时间是 \(O(B^2)\) 的。不能直接处理,我们可以用类似于回答询问的方法进行值域分治,由于处理一次是平方级别的所以可能不带 \(\log\)

首先注意我们虽然分治值域但是 不改变 \(a_i\) 的下标顺序 ,设分治的值域是 \([l,r]\) ,找到这些数的中位数,记为 \(mid\),目的是让 \([l,mid]\)\((mid,r]\) 各占有该区间的 \(\frac{1}{2}\) 个数。递归回来后我们考虑算些什么,对于这一层的所有区间 \([L,R]\) ,它是被拆开了分治下去的,现在我们考虑将他合并回来,也就是在 \([l,mid]\) 中查找 \([L,R]\) 对应的区间和 \((mid,r]\) 中对应的区间拼起来(和回答询问一样的方法),最后就得到了块内每个区间的集合。

复杂度证明:\(T(n)=2T(n/2)+O(\frac{n^2}{2})\),发现是 \(\frac{n^2}{2}+\frac{n^2}{4}+\frac{n^2}{8}...=n^2\),所以每个块复杂度 \(O(B^2)\) ,预处理复杂度 \(O(nB)\) ,由数学知识可知 \(B=\sqrt q\) 时和最小,时间复杂度 \(O(n\sqrt q)\)

例十四

题目描述

给定一个 \([0,n-1]\) 的排列 \(p\) ,每次询问 \((i,j)\) 返回 \(p_i,p_j\) 最多 \(4269\) 次询问,推出这个排列。

\(n\leq 2048\)

构造方法

这个询问次数是比 \(2n\) 略大一些的,应该是一个线性的做法吧。

发现如果我们找到了 \(0\) 在哪里那么再花费 \(O(n)\) 的时间就做完了,下面介绍两种得到 \(0\) 的方法:

方法一

考虑随机算法,第一次随机取一个位置,求出他和所有数的或,找到或值最小的那些数,\(0\) 一定存在于这些数中。然后再随机一个位置,求出它和有可能成为 \(0\) 的数的或,再找到最小值 \(....\) 重复这个过程,知道最小值只有一个位置取得到,那么他就是 \(0\) 了。

复杂度取决于 随机,因为我们随机一个数 \(1\) 的期望个数是 \(\frac{11}{2}\) 的,所以剩下的数只可能在选出来的数二进制位为 \(0\) 的位上取 \(1\) ,那么每次的个数就相当于开了根号,所以是 \(O(n+\sqrt n+\sqrt{\sqrt n}...)\) 的,常数有点大而且是期望复杂度。

方法二

这是一个严格线性的做法,我们看每个数是否能成为 \(0\) ,但判断这个不需要询问和其他所有数的或值,我们从左往右访问位置数组,有可能成为 \(0\) 的是位置 \(a\)\(b\) ,新加入的是 \(c\) ,那么有这些情况:

  • \(a|b>a|c\) ,那么 \(b\) 必然不为 \(0\) (你可以假设 \(b\)\(0\) 然后反证)
  • \(a|b<a|c\) ,那么 \(c\) 必然不为 \(0\)
  • \(a|b=a|c\) ,那么 \(a\) 必然不为 \(0\),如果 \(a=0\) 那么 \(b=c\) 矛盾。

分析一下询问次数,\(a|b\) 是可以不必次次都问的,看起来是 \(O(2n)\) 的但是情况 \(3\) 出现的概率很小,最后还要判断 \(a,b\) 中哪一个是 \(0\) ,随机一个位置,如果或值不一样就区分出来了。

为了防止被卡,位置数据是可以随机的,那么想卡都卡不了你了。

例十五

题目描述

有一个数组 \(A\) ,你每次可以询问一个位置,会告诉你这些位置上数字的或。

你需要在 \(13\) 次操作内得到所有 \(p_i\) 表示除了 \(a_i\) 以外其他所有数字的按位或。

\(n\leq 1000\)

构造方法

首先将一种

posted @ 2021-01-03 17:13  C202044zxy  阅读(402)  评论(0编辑  收藏  举报