zoukankan      html  css  js  c++  java
  • 构造题训练

    由于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)个数进行比较即可求出最大/最小值。

  • 相关阅读:
    微服务-分布式事务解决方案
    Python cannot import name 'Line' from 'pyecharts'
    powershell 基础
    Linux SSH config
    神奇的Python代码
    GitHub 中 readme 如何添加图片
    Linux 笔记(自用)
    Anaconda 安装 TensorFlow ImportError:DLL加载失败,错误代码为-1073741795
    Ubuntu 分辨率更改 xrandr Failed to get size of gamma for output default
    Git入门教程 Git教程入门
  • 原文地址:https://www.cnblogs.com/ctmlpfs/p/14287117.html
Copyright © 2011-2022 走看看