zoukankan      html  css  js  c++  java
  • [正睿集训2021] 构造专练

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

    例一

    题目描述

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

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

    构造方法

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

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

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

    例二

    题目描述

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

    (nleq 10)

    构造方法

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

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

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

    例三

    题目描述

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

    (nleq 1000)

    构造方法

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

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

    在这里插入图片描述

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

    例四

    题目描述

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

    (nleq 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) 连向所有 (iin[k+1,2k])(2k+2) 连向所有 (jin[1,k]) 。这样就用完了所有的边并且得到了 (k+1) 棵树。

    例五

    题目描述

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

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

    (nleq 40)

    构造方法

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

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

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

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

    在这里插入图片描述

    例七

    题目描述

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

    (nleq 1000)

    构造方法

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

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

    例八

    题目描述

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

    构造方法

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

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

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

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

    例九

    题目描述

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

    (5leq nleq 10^5)

    构造方法

    首先我们知道 转化是可逆的 ,你怎么来的还是能够怎么回去。所以我们可以找到一个中间状态,让初始环可以转化到它,目标环也可以转化到他(类似于双向 ( t 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) 。每个点的权值为两个子节点的与非值(即先做 ( t and) 运算再取反),初始权值不定,你需要钦定尽量多的叶节点使其满足下列条件:

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

    (nleq 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(nlog n))

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

    例十一

    题目描述

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

    (nleq 500,mleq2000),询问次数 (20000)

    构造方法

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

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

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

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

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

    在这里插入图片描述

    例十二

    题目描述

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

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

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

    (2leq n,mleq 1000)

    构造方法

    一般这个加减的题你要考虑什么东西是定值,这道题有点奇怪,对于任意一个 (3 imes 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}}) 都已经被合并出来过了(不能有其他的东西)

    (nleq 2^{12},Qleq 2^{16})

    构造方法

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

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

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

    例十四

    题目描述

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

    (nleq 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) 以外其他所有数字的按位或。

    (nleq 1000)

    构造方法

    首先将一种

  • 相关阅读:
    TabControl添加关闭按钮
    Windows & RabbitMQ:集群(clustering) & 高可用(HA)
    Windows & RabbitMQ:Shovel
    15项最佳电子产品影响人类未来
    收藏很久的开关电源书籍
    我也不想这样(转载)
    vbs 脚本轻松搞定JDK的环境变量配置
    开关电源基本知识
    浅谈软件开发定律系列之帕金森定律(Parkinson’s Law)
    堕落的时候看看——清华大学老师的一席话
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14226126.html
Copyright © 2011-2022 走看看