赛时复盘
总体上这场考试过程比较顺畅,没有前段时间ybt 14连战的时候有几天那种看了20分钟题就窒息的感觉。
T1构造,T2看题+数据范围
1
0
6
10^6
106 明显SA,T3本质不同子序列,一开始看成本质不同子串了,以为这一天一道SA一道SAM。另外我一开始看到多组询问看到临时插入就开始想什么可持久化平衡树,现在看来真的是学愚了(笑)。
大致策略就出来了:直接研究T1,然后研究一下T2,争取哈希二分搞一个60~72分(SA太久没写了,加之前面要做构造后面要看SAM,所以一开始就放弃了用SA),T3再重新考虑一下,T2和T3写完暴力就依情况分配时间了。
T1看数据范围只有10000 ,而相比之下点数很少(500),说明此题限制不是时间复杂度而是方案优秀性,加之这题开放度高,进一步说明这题应该是一个思维题 ,换句话说,实在很适合我。
对于前25分,路径数少于可用点数,直接一条链完事。
然后考虑树上怎么能产生一个长度为3的路径。假设起点要比终点深,对于某点 来说,一共只有两种可能:自身—1级祖先—2级祖先—3级祖先,自身—1级祖先—2级祖先—2级祖先的其他子节点。 也就是一个点的贡献就是0,1,2三种可能。
为了方便,上完全二叉树考虑一下性质,对于每一个深度大于等于2的节点都能产生1的贡献,每一个深度大于等于3的节点能再产生1的贡献,一棵深度为
n
n
n 的完全二叉树能产生
(
2
n
+
1
−
12
)
(2^{n+1}-12)
(2n+1−12) 条路径,非常够用。
由于思考的时候是从完全二叉树出发的,所以达到所需路径数的方式就是从树上删点。由于完全二叉树非叶节点有2个儿子,所以删掉一个叶结点恰好会减少2的贡献,这一来,如果路径数是偶数,就可以先倍增的一层一层删,然后一个一个点删。
考虑路径数是奇数的做法。删除是不行了,考虑怎么增加。如果增加叶结点,不管这棵树什么形态,总是增加2的贡献,所以不能加叶结点。为了让问题简单一些,假如在根的右儿子上多插入一个儿子,会增加5的贡献,于是再删掉两个叶结点就是+1,这就凑出来了。
然后在上厕所的时候意识到二叉树虽然产生的路径多但是用点也多,过不了。但是这个模型还是很有意义的。
考虑一下菊花图,假设根节点有
n
n
n 个儿子,每个儿子又有一个儿子,这个时候长度为3的路径数就是
n
×
(
n
−
1
)
n\times(n-1)
n×(n−1) 条,这就避开了点不够的问题。现在考虑怎么得到答案。
和之前的二叉树一样,对这个图的叶结点操作只会影响它自己产生的贡献,问题是这里的每一个叶结点删掉会直接把边数减少
n
−
1
n-1
n−1 ,太粗糙了,删除不好做,考虑加点。由于通过儿子的个数已经对询问分层了,这个时候不同层之间的差距只有
2
n
2n
2n 。在经过了一系列头脑风暴之后,意识到构造这个图的基础需求也不过
(
2
n
+
1
)
(2n+1)
(2n+1) 个点,由于询问最大也就10000,
n
n
n 最多只有100就够了,用
(
4
n
+
1
)
(4n+1)
(4n+1) 个点是完全可行的。这就可以用上最开始那个链的性质了,构造链的时候从第三个点开始就是一个点产生1的贡献,而这个图的叶结点深度就是2,所以先找一下询问路径数的前驱,在此基础之上,只需要随便挑一个儿子一直往下造链就行了。
此题的构造部分代码只有9行。
这道题从链到二叉树到菊花图到想出正解花了大约70分钟,总共的考试时间已经过去大约95分钟了。写完正解之后保险起见写了个dfs检验,并且直接把询问从1枚举到10000检查了一下,没问题。此时花了125分钟。
我在检验之前,居然还花了5分钟犹豫写验证函数求这棵树长度为3的路径数的时间是不是有点太浪费,因为我对于这个检验的写法满脑子想的都是写点分治…我自己都吐槽不动了(捂脸)
然后是T2,字符串长度1或者全程同一字符显然就是1,长度为2直接对半分就行了。三个特判先打上。最大值最小想到二分答案加验证,然后就卡在了验证上。首先二分的是最大值,然而枚举数组 k k k 显然不可行,用什么均值不等式柯西不等式也不能维护和为一的性质;经过了很长时间,发现还可以反过来想,假设每一组的最大值都是 m i d mid mid ,求 k k k 的解是否合法,这个可以用高斯消元。但是问题在于一是正确性难以验证,二是无解难以处理,三是求出负数更难以处理。尽管没有子任务制,考虑到这种做法正确性过于惨不忍睹,干脆先不写了,去看T3了。这个时候大约过了180分钟。
T3发现了是子序列而不是子串,说明绝对跟字符串算法没关系了,应该就是dp,让人长舒了一口气。上来第一反应就是求本质不同子串不如求本质相同子串然后走一个补集,然后就开始思考怎么求本质相同子串(事实证明这是一个非常糟糕的决定,求本质不同子串其实很简单…),想了很久都没想出来,于是写了个背包。问题是这个背包的空间的指数级的,但是也没啥好点子了。剩下了30分钟,然而并没有再想出什么东西。
赛后
都这样了居然还能挂8分,T2由于思考
k
k
k 想疯了导致字符串长度为2的时候输出了个0.5,只能说是有点太傻。看了题解之后发现最大的问题是T2需要用SA变成序列问题分治,也就是说这个SA很大程度上没法用二分哈希替代。以及这题用高斯消元真的能得到60,令人无语。T3看了一眼别人的暴力代码发现自己真的是傻了,这题设计一个普通的dp其实并不复杂,因为重复的部分一定是出现在上一次以这个字符结尾之前的,所以可以直接 O(n) 完成dp。结合上一次ACM,只能说dp能力属实太水了。不管是SA还是dp,总之就是多学多练吧。
另一个问题就是这场比赛的反复上头问题,什么验证函数写点分,什么可持久化平衡树,什么本质不同就SAM,这种问题影响不大,但是误导还是应该减少到最少。所以打比赛没有知识点指向的时候,看题的过程中真的 应该冷静一点。