zoukankan      html  css  js  c++  java
  • SDOI2019 Round2

    这鬼家伙已经咕了好久了……

    SDOIR2的题目挺好玩的~

    快速查询(???)

    不难发现所有的操作都可以通过区间打Tag实现

    那么可以维护两个标记(a,b)表示序列中的数为(x)时实际表示的值是(ax+b)。对于一个单点赋值操作把值(x)变为((x-b)a^{-1})放进数组里面,对于全局赋值操作直接把数组清空。只清空在上一次清空到这一次清空之间被修改的数组上的位置,那么清空的复杂度一定不会大于前面所有操作的操作次数,复杂度就是对的。

    至于怎么存这个数组可以离散化也可以像我一样很呆地手写哈希表

    代码

    染色(DP)

    有一个显然的暴力:设(f_{i,j,k})表示填满前(i)列、第(i)列两个元素的颜色分别是(j,k)的方案数。复杂度三方一分都没有……

    考虑在这个DP的状态上压掉一维。注意到如果我们称至少有一个位置有颜色的列是重要列,那么如果我们可以预处理重要列与重要列之间的转移,我们就只需要记录最后的重要列上(0)位置处填上的颜色即可,那么DP的复杂度就变成了(O(n^2))

    感觉很不错,我们考虑两个重要列之间的可能状态。设(x,y)是其中一个重要列的颜色,考虑另一个重要列的颜色,有以下几种情况。在下面的叙述中用(?)表示不与(x,y)相同的颜色,一横排的若干个矩阵表示这些矩阵的方案数是相同的。

    (left(egin{align*} x && ... && y \ y && ... && x end{align*} ight))

    (left(egin{align*} x && ... && x \ y && ... && y end{align*} ight))

    (left(egin{align*} x && ... && ? \ y && ... && x end{align*} ight)) or (left(egin{align*} x && ... && y \ y && ... && ? end{align*} ight))

    (left(egin{align*} x && ... && ? \ y && ... && y end{align*} ight)) or (left(egin{align*} x && ... && x \ y && ... && ? end{align*} ight))

    (left(egin{align*} x && ... && ? \ y && ... && ? end{align*} ight))

    (g_{i,0/1/2/3/4})表示这五种状态中间全为(0)的列数为(i)时的填充方案数,转移枚举最后一个全为(0)的列即可,系数有点复杂,可以使用矩阵优化但是我这么无脑的人肯定是直接大力写式子啊

    然后去掉头尾的全(0)列(因为它们的方案数可以直接贡献到答案里),我们就可以每一次枚举两列,用上面求出的(g)数组进行转移了。复杂度(O(n^2))可以通过96pts。

    所以最后的4pts当然是打表啦考虑优化DP。

    当你写出式子之后就可以发现:其中的所有操作稍作变换可以变为“快速查询”中的区间加、区间乘、单点修改、单点查询、询问所有的和的操作。写一遍不需要离散化的快速查询配上这个DP就可以了。

    96pts 100pts

    世界地图(最小生成树、虚树)

    需要求一个前缀和一个后缀的最小生成树,我们可以先预处理好前缀和后缀的生成树,然后考虑连接前缀和后缀之间的边的影响。

    如果已经有边连接了前缀和后缀,那么如果再加入一条连接前缀和后缀的边,那么一定要删掉两个经度为(1)的点在前缀最小生成树上的路径的最大边权的边,或者两个经度为(m)的点在后缀最小生成树上的路径的最大边权的边。那么可以发现前缀MST和后缀MST上有用的边只是经度为(1)(m)的点的虚树上的边。

    那么我们预处理前后缀MST的时候也使用同样的方法,计算完MST之后把两边边界上的点在MST上的虚树记录下来,注意到虚树上一条边的边权可以直接赋为这两个点对应MST路径上的所有边的最大边权,那么一个MST中有效的边的数量就是(O(n))级别的,每一次合并的复杂度就是(O(nlogn))

    那么这样做就可以得到一个(O((m+q)nlogn))的做法。

    代码

    热闹的聚会与尴尬的聚会(构造)

    题设相当于((p+1)(q+1) geq n)

    考虑如下构造方式:每一次在图中删去度数最小的点和它所连的边直至图为空,设每一次删去的点的序列为(p_1,p_2,...,p_k),每一个点被删去时的度数为(d_1,d_2,...,d_k),那么周日聚会就邀请(p_1,p_2,...,p_k),周六聚会就邀请(d_i)取到最大值时对应的图上的所有点。

    证明:序列(p)的所有点显然独立,而(d_i)取到最大值时选择的所有点的热闹度就是(max d_i)。因为(sumlimits_{i=1}^k (d_i + 1) = n)(k(max d_i + 1) geq sumlimits_{i=1}^k d_i +1 = n),所以((k+1) (max d_i + 1) geq n + 1),所以这样的选择方案是合法的。

    删点使用set维护就可以了。

    代码

    移动金币(阶梯Nim、DP)

    把第(i)个金币和第(i+1)个金币之间的空格个数看作第(m-i)节阶梯的石子数量,那么这个显然是一个阶梯Nim,有效的石子是奇数节的石子,而偶数节的式子对胜负没有影响。

    所以我们只需要考虑奇数节的石子,而对于偶数节的石子直接组合数分配。计算先手获胜的概率似乎并不好算,但是比较好算的是先手必败的方案数,因为这意味着奇数节阶梯石子异或和为(0),也就是每一位恰好出现偶数次。发现(n)并不是很大,我们可以直接按位背包。复杂度是(O(mnlogn))的,但是实际跑得很快。

    代码

    连续子序列(记忆化搜索)

    发现最近的省选很喜欢出思维的搜索题(隔壁TJOID2T3就是,ZJOID1T1或许也可以算)

    之前有一个类似于(0110)缩成(0)(1001)缩成(1)的方法,但是似乎很难避免算重,然后发现有一种更优秀的做法……

    考虑T.M.序列的一个性质,可以参考OEIS A010060:令初始序列为(0),然后用(01)代替(0)(10)代替(1),做无限次就可以得到T.M.序列。

    那么如果一个序列是T.M.序列的子序列,也就一定能够通过(01)填充为(0)(10)填充为(1)的方式还原为(0)

    那么我们考虑dfs(s , k)表示当前前缀为s、后面还需要填充k个字符的方案数,转移可以考虑枚举填充的方式:有可能是s的(x(2 otmid x))(x+1)字符组合,也有可能是(x(2 mid x))(x+1)字符组合,即在前面加上一个字符使得(s[1])能够和这个字符组合,被一个单独的字符填充。

    不难发现当一个串的长度(>3)时这样的划分方式是唯一的,证明可以不妨假设两种填充的方式都成立,那么如果在第一种填充方式下填充出了前缀(00)或者(11),那么在第二种填充方式下就会填充出前缀(000)或者(111),这是不合法的;而如果填充出了(01)或者(10),那么在第二种填充方式下根本不可能存在合法的字符串满足条件。

    而当串长度(leq 3)时,比如说串(010),它就可以被填充为(01|01)或者(10|10),这两种方案都合法,但是这个字符串只能被计数一次,所以这些地方需要特判。

    注意到当(|s|=1)时方案数只和(k)的大小有关,在这种情况下我们可以进入另一层递归。在这层递归中使用记忆化,那么总的状态数就是(O(logk))级别的,而solve函数的递归总数是(O(|s|))级别的,所以复杂度很小可以通过本题。

    代码

  • 相关阅读:
    数据库学习 ORA12545:因目标主机或对象不存在,连接失败
    Oracle创建命名空间和新用户
    创建一个命名空间
    修改用户密码
    Oracle启动
    数据库表中列类型的修改和ALTER的用法
    修改列类型
    IE6/IE7/IE8/Firefox/Chrome/Safari的CSS hack兼容一览表
    css:fixed定位兼容不同系列不同版本的浏览器包括IE6.0
    jQuery Google Charts一个封装google chart api的jquery插件 饼状图
  • 原文地址:https://www.cnblogs.com/Itst/p/10991758.html
Copyright © 2011-2022 走看看