• 小W的增长信心赛~Solution以及弱省蒟蒻出题人的感想


    作为一个辣鸡,最近找不到做题的动力,而且被友校同学出的题目摁在地上摩擦,于是我在Luogu出了一套水题礼尚往来,这里是这套水题的题解。
    Luogu上的地址:点我

    Problem A:对战怪函数(Strange)

    算法一

    注意到区间的最大值一定在1~n之间,于是O(n2)枚举区间,然后对每个区间O(n)计算区间最大值,记录每个区间最大值出现的次数,O(n)计算答案。
    期望得分:20分

    算法二

    在算法一的基础上,在枚举区间的同时,计算区间最大值。
    期望得分:40分

    算法三

    注意到,以一个数为最大值的区间,左端点一定在这个数往左最近的一个比它大的数的右边,右端点一定在这个数往右最近的一个比它大的数的左边。因为所给的数列是一个排列,所以不会有相同元素,所以我们对于每一个元素,找到它左边最近的比它大的数的位置i,找到它右边最近的比它大的数的位置j,设这个数本身位置为k,那么显然以这个数为最大值的区间数量为(ki)(jk)个。
    因此对于每一个位置,暴力向左右寻找ij,在随机数据的情况下期望时间复杂度是O(n)
    期望得分:50分

    算法四

    在算法三的基础上,用一些神奇的东西(如线段树、树状数组、set或一些本蒟蒻不知道的奇技淫巧)维护,时间复杂度为O(nlogn)
    期望得分:80分

    算法五

    在算法三的基础上,想办法加速查找ij的过程。
    有一个利用一种叫单调栈的数据结构的算法:从左往右依次将元素入栈,对于每个元素记录两个值ij,一开始将i初始化为自己的位置编号。在将一个元素放入栈中前,如果当前栈顶元素比它小,那么让栈顶元素出栈,并将这个栈顶元素的i赋值给当前元素的i,而将“当前元素的位置1”赋值给这个栈顶元素的j,一直重复直到栈中没有元素或栈顶元素比它大为止,然后再将当前元素放入栈中。将所有元素入栈后,栈中所剩的元素的j值就是n
    实际上我们要查找的ij就是这里我们维护的i1j+1,至于为什么这个算法是正确的,应该很容易理解,而每个元素最多入栈、出栈各一次,所以这个算法的时间复杂度O(n)
    期望得分:100分

    Problem B:狂虐位运算(Sum)

    算法一

    暴力计算所求的异或和,时间复杂度O(22n)
    期望得分:20分

    算法二

    我们可以计算每一位对答案产生的贡献,从右往左第i位对答案产生的贡献显然为在l~r这些数中,这一位上为1的数的数量×这一位上为0的数的数量×2i1。于是暴力计算每一位对答案的贡献,时间复杂度为O(n2n)
    期望得分:40分

    算法三

    我们只需想办法求每一位上为1的数的数量就可以了,而在固定区间[l,r]内满足条件的数的数量就是在[0,r]内满足条件的数的数量减去在[0,l1]内满足条件的数的数量,因此就把问题转化为:在[0,x]中有多少个数在某一位上为1
    考虑数位DP的思想,对于x中每一个为1的位(假设是从右往左第i位),考虑这样的数:在这一位之前和x完全相同,这一位为0,后面位置不限的数。显然这样的数共有2i1个,那么这样的数中有多少个数在某一位上为1呢?
    我们发现,对于在x中当前位之前的所有为1的位上,都有2i1个满足要求的数,而对于在当前位之后的所有的位上,都有2i2个满足要求的数(因为每个位置是0或是1的概率完全相同,所以数的个数为2i1/2=2i2)。这样就可以计算出这样一个区间内的在每一位上为1的数的数量。然而这样计算会缺漏一个数:x,解决方法是在最后添加一个第0位,它给予左边每个为1的位1的贡献。知道了这一点之后,我们就可以计算出每一位上为1的数的数量,然后就可以反推出每一位上为0的数的数量了,时间复杂度为O(n2)
    如果听不懂我上面在说啥,讲一个例子:x=(10100)2,那么我们考虑以下区间:
    对于右往左第5位,我们可以计算区间(00000)2~(01111)2
    对于右往左第3位,我们可以计算区间(10000)2~(10011)2
    最后我们还需要计算(10100)2
    这样应该就能很明了地看出应该如何计算了。
    期望得分:70分

    算法四

    在算法三的基础上,分别计算从左往右和从右往左的贡献,并在计算的同时累计贡献,这样就可以做到O(n)的复杂度。
    期望得分:100分

    Problem C:修建地铁线(Subway)

    算法一

    分析题意,题目中要求的显然是,将n个点连成一棵树,使得这棵树能用且至少需要用k条没有公共边的路径覆盖的方案数。那么当k=1时,只有当树为链时合法,那么答案显然为n!/2,直接输出这个结果即可。
    期望得分:20分

    算法二

    进一步分析,能用且至少需要用k条没有公共边的路径覆盖意味着什么。我们考虑给定一棵树,求该树至少需要用多少条没有公共边的路径覆盖。注意到这就是图论中著名的一笔画问题的扩展,我们显然可以证明,当树中有2k个度数为奇数的点时,该树能用且至少需要用k条没有公共边的路径覆盖。因此采用搜索的方法枚举方案或者打表可以通过测试点#3~#5。
    期望得分:30~50分

    算法三

    通过算法二中的分析,注意到这是一个有关于构造一些点满足某些度数限制的情况下的树的方案数的问题。这时候需要用到一个叫做Prufer序列的知识。
    对于一棵无根树,我们可以构造出它对应的Prufer序列。构造方法是:每次将编号最小的叶子节点删去,然后将该点所连接的那个节点编号记录在序列中,这样重复n2次后,得到一个长为n2的序列,这就是这棵树所对应的Prufer序列。
    可以证明树和Prufer序列之间是一一对应的关系(证明可以看看Matrix67大佬的文章)。那么当然也存在一种方法能从Prufer序列反回去构造无根树,不过由于篇幅限制这里就不讲了。
    从上面大佬的文章中还可以得出一点:若一点在Prufer序列中出现d次,那么这个点在对应的树中的度数为d+1。于是我们回来观察这一题,我们发现要求的是有2k个点度数为奇数的树的个数,那么通过Prufer序列,问题转化为:求有2k个元素出现偶数次的Prufer序列个数。我们想到用递推的方式解决,令f(i,j)为长为i的序列中,有j个元素出现偶数次的方案数,那么有:
    f(i,j)=(nj+1)f(i1,j1)+(j+1)f(i1,j+1)
    边界条件为f(0,n)=1,答案为f(n2,2k),算法的时间复杂度为O(n2),可以通过这一道题。
    期望得分:100分

    Problem D:攻入核系统(System)

    算法一

    用DFS或BFS预处理每个点到其他点的距离,然后暴力修改和查询,时间复杂度O(n(m+n))
    期望得分:20分

    算法二

    每次修改和查询时,暴力搜索周围在范围内的点,进行修改和查询。
    期望得分:30分(多10分是因为#4太水)

    算法三

    对于测试点#3~#4,暴力分类讨论统计答案。
    期望得分:40分(配合算法一)

    算法四

    我们先将无根树转化为有根树,然后发现,当k=1时,涉及到的点分为3个部分:询问点的父亲,询问点,询问点的儿子。我们发现一个点的儿子在树的BFS序上是连续的,所以先用BFS预处理,然后用线段树维护区间和即可。
    期望得分:60分(配合算法一、三)

    算法五

    在算法四的基础上,我们讨论k=2的情况,我们发现涉及到的点分为5个部分:询问点的父亲的父亲,询问点的父亲,询问点父亲的儿子,询问点的儿子,询问点的儿子的儿子。每个部分中的点在树的BFS中都是连续的,所以用线段树维护即可。
    期望得分:100分

    感想

    啊,作为一个弱省的蒟蒻,能出出这种题很不容易啊……以及第一题和第四题的数据真的好难出啊,当时就觉得,就算出成这样估计也会被艹的很难看,于是就弃疗了……
    (结果因为各种撞题成功中道崩殂……)
    不过还是感谢验题人一直以来的坚持,让我多考虑了几种可能会被艹的姿势(雾),而且验题人貌似写出来了第四题线性做法,然而我并不会,所以题目数据范围就没开到那么大。
    至于题目英文名的四个S……本来的英文名其实并不是这样的,然后突然想到可以这样设计,于是就改成这样咯~S有Super之意,也算是给诸位图一个吉利吧。
    (标题写成Solution也是故意的hhh)
    那么,就这样啦,感谢大家对我比赛的支持!以及感谢你一直看到最后!

  • 相关阅读:
    cocos2d-android学习四 ---- 精灵的创建
    Think In java 笔记一
    管理文件夹
    Android Studio Mac 快捷键整理分享
    协同过滤
    POJ 3281(Dining-网络流拆点)[Template:网络流dinic]
    JS经常使用表单验证总结
    js中的Call与apply方法
    (转)WPF控件开源资源
    五年北京,这个改变我命运的城市,终于要离开了(转)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793542.html
Copyright © 2020-2023  润新知