由于NOIP之前没有怎么做过构造,导致吃了大亏。
构造(交互)基本问题:
有一些数,假设有(n)个,有(m)个被标记,我们可以询问一个集合。我们可以得知这个集合内是否存在被标记的数。
要求次数(O(mlog_2n))
做法1:分治
考虑分治到当前区间([l,r]),询问([l,r])有没有被标记的数。
如果没有,则返回,否则继续分治。
时间复杂度等于所有被标记的数到根的链的并,是(O(mlog_2n))级别的。
做法2:二分。
按照编号序(1...n)进行二分。每次点亮一个前缀,二分出一个最大的(l)使得([1...l])不存在被标记的数。
则(l+1)肯定会被标记。
然后把([1,l+1])截掉后继续二分。
操作次数是(O(mlog_2n))级别的
构造例题1:制作菜品
考场上最后1h才发现此题结论,然后没写完。
题目中(m geq n-1)的部分分占比小,(m=n-2)的部分分占比巨大。
由于二元关系,可以想到把菜肴连边。
当(m=n-2),我们会发现连出的边会形成至少(2)个连通块。这是因为每插入一条边就会减少一个连通块。
我们会发现至少有一个连通块满足边数小于点数,即边数等于点数(-1)。
这样子我们就转化为了(m=n-1)的情况。
当(m=n-1),wlog把(d)排序,由于抽屉原理(d_1<k)
可以证明(d_1+d_ngeq k)。
使用反证法。假设(d_1+d_nleq k-1,d_nleq k-1-d_1)
(d_1+...d_nleq (n-1)(k-1-d_1)=(n-1)k-(n-1)-(n-2)d_1<(n-1)k),矛盾。
然后可以把第一个材料消掉,(m--,n--)。
当(mgeq n)由抽屉原理,(d_ngeq k),所以把(d_n-=k)即可把(m--)。
可以用set维护。
当(m=n-2)时可以使用dp寻找(d)的和等于((|S|-1)k)的集合(S)。
显然这是个经典的背包问题。可以用bitset优化。
考虑把所有物品的重量减去(k),就不用记录选择了多少个数。
例题2:新年的dog划分
考虑生成树。
考虑bfs生成树,设生成树当前集合为(S),外集合为(T)。
每次把一个点拓展一下。
找到这个点往外连出且没有被访问过的点。
然后把这个点往外的边全部拿出来,把(S->T)边全部删除,二分出最后一个插入后仍然不连通的点(x)
则(x->a[x+1])(其中(a)为二分序列)有边。
然后把(x->a[x+1])插入队列。
这样子重复(n)次就得到了一个生成树。
判定生成树合法可以只保留生成树,删除生成树上每一条边,判定(n-1)次。
如果我们删除某条边后图仍然连通,则图中有奇环,否则没有。
例题3:I君的商店
先考虑sub4做法。
由于题目给定了(0/1)个数的奇偶性,所以考虑把数组分成长度为(2)的块。
发现每个块内只可能是全(0)或者全(1)。
发现不能两两比较,所以考虑拿出三个比较。
由于题目中给定最大值为(1),所以考虑把(1)作为(T)集合,每个块作为(S)集合。
如果当前块比(1)小,则块内的数都是(0),否则都是(1)。
这样子我们就可以快速确定一个块内元素的值。
可以以这个为依据二分。
前面(3)个sub,发现题目的操作如果(card(s),card(t)=1),则我们等同于比较整数。
这样子我们可以使用这个操作把所有数排序再使用sub4的算法。
这样子就有76分了。
考场上拿到76分就知足了。
我们真的要把所有整数排序吗?
考虑两个数(x,y),前面提到,我们可以花费(2)的代价找到它们的顺序。
根据sub4的做法考虑比较(x+y,1)的值。
如果返回(<),则(x+yleq 1)否则(x+y geq 1)
由于(x,ygeq 1)我们会发现,当返回(<)时,(x,y)最小者必定等于(0)。
否则(x,y)最大者必定等于(1)。
这样子我们可以花费(7)的代价确定一个数的值。
但是我们并没有利用sub4的做法。
根据sub4的做法,随便拿出两个数(x,y)和(z)比较,花费2代价令(x<y)
如果返回(<),则根据前面的分析(x)必定等于(0)。
否则分类讨论可得(ygeq z)。
维护一个有序序列(S),把(y)插入(S)的末尾。
最后会剩下一个数,可以在(S)上二分插入这个数。
然后在(S)上二分即可得到所有数的值。
例题4:apio gap
sub1由于次数是(n/2),并且每次可以确定两个值。
考虑每次确定(a_i,a_{n-i+1})
调用题目给的函数(MinMax(a_i+1,a_{n-i+1}-1))即可求出(a_{i+1},a_{n-i})的值。
对于sub2考虑抽屉原理。
把(a_n-a_1)视为(a_n-a_1)个物品。(a_i-a_{i-1})视为一个抽屉。
则肯定有一个抽屉包含至少(frac{a_n-a_1}{n-1})个物品。
考虑把数组分成大小为(frac{a_n-a_1}{n-1})的块。
先调用一下(MinMax)函数求出(a_n-a_1)的值以确定块大小。
发现我们的答案一定不会出现在同一块内。且只有块内的最大/最小值有资格成为答案的两端点。
记录以前的最大值即可求出答案。
例题5:uoj504
例题6:loj3332
例题7:loj 2875
显然直接按照(min,max)的定义进行比较,操作次数为(O(2n))
考虑这么一个事实:我们把数组分成长度为(2)的块,块内比较一下。
设块内较小数为(m1),较大数为(m2),则只有(m1)集合内的数可能成为最小值,(m2)集合内可能成为最大值。
我们只需要把(m1)的数提取出来,(m2)的数也提取出来。
把这两组每组(n/2)个数进行比较即可求出最大/最小值。