CF1919 (Hello 2024) D,E,F1题解
没打,VP了,但只会做前四题。
感觉这场题还不错,记录下D,E,F1,剩下的就没看了。
D
题意:有一颗标准二叉树(有左儿子必有右儿子),连接每个左右儿子的边权一个为0一个为1,中序给出所有叶子到根的距离,问你是否存在这样的二叉树。
Solution1:VP时想出来的:
显然有叶子距离是0而且只有一个,但是距离为1的就可以有好多个(左偏毛毛虫就可以)。我发现如果从这个0叶子开始,刨去这个沿着0边向上一直到根的链,那么会把原来的树分成好多子树,而且,每个子树的根都有一个1边连向这条链。那么,相当于那些子树所有的叶子距离都加过这个1边,我让所有的点距离减去1,那么每棵子树就变成了子问题,每棵子树内必然会有0叶子,再把这个0链刨去,再分成更小的子树。
在中序序列中,就体现在第一次用0分割序列,然后用1分割新序列,然后2,3,直到把所有序列割完。不合法情况比如 0,1,2,4,2 ,在用2割完之后,出现了一个只有4的子树,但是按理说这时候所有子树都应该有个3,所以不合法。
分割序列维护起来挺麻烦的,感觉想了老半天才开始写。
Solution2:官方题解,更好写:
标准二叉树必定会有一对兄弟均为叶子,删去它俩,它们的父亲变成叶子,重复删,可以删完整棵树。
这对兄弟的差值肯定是1,而且中序相邻。但显然,这不代表中序相邻且差1的两点一定是兄弟。
但这没关系,我们考虑删掉两点之后会对序列造成什么改变,发现父亲变成了新叶子,父亲的距离和原来的0儿子一样的,所以其实较小的数还在,只是较大的数凭空消失了。
所以,我们可以从序列中最大的数开始删,可以用链表来维护,每次最大的数旁边有相邻且差一的数,就可以把大数删掉,直到删的只剩一个0即可。中途发现一个数左右和它差的都不是1,那就不合法。这个solution更好写,也更好想。
E
比F1难。
题意:数列a每个数绝对值为1,s为前缀和序列,告诉你s中每个数字出现次数,求数列a有多少种。
网上搜了搜题解,有用记忆化搜索切成小区间的,也有用分段dp的(从上往下扫,\(dp[i][j]\) 表示扫到 i 已经分了 j 个区间,然后每次枚举新增单点区间数量),但都不咋看得懂哈哈。
把官方题解看懂了,很好写的n方:
Solution:
所有前缀和的轨迹其实就是经典的加1减1一条龙折线(高度即为前缀和),我们枚举折线的最后一个点的高度 t(t=所有a的和),s中每个数出现次数是折线的每个高度的出现次数。
我们先构造一个从0开始到最大数,再下降到 t 的折线。在高度为h的点后面下降一格,再上升一格,即增加一个 \([-1,1]\),就会多出来一个h-1和h,我们就利用这样的添加方式,来将一开始的折线一点点改造成符合题意要求的折线。仔细思考会发现,序列a的每一种方案都对应着折线的构造方案。
比如s中最高的点出现了3次,你的折线里只出现了一次,你就需要增加两个这样的 \([-1,1]\),并更新你折线中h-1的出现次数。然后如果s中h出现了4次,你的折线里出现了两次,你的两个 \([-1,1]\) 可以放在同一个点后面,也可以分别放,这是一个第一个点位置固定的插板问题,用组合数计算一下。由于每个高度问题独立,所以把所有高度的构造方案数乘起来,即为 t 对应的答案。
枚举 t ,再从高往低处扫,累加答案,复杂度n方。
不合法情况:你的折线h出现次数比s中还多,或者你折线里h出现次数为0但是s中有h(没法构造了这样就)。
F1
题意:给你两个数列 \(a\) 和 \(b\),\(a_i\)最多可消耗\(b_i\),多出来的部分可以加给 \(a_{i+1}\),贡献是所有\(a_i\)的消耗量。每次询问修改一对\(a_i,b_i\)重新计算贡献。
VP时只想到从前往后扫看哪些位置会断,没想到应该从后往前扫看哪个位置不会断。
Solution:
显然由于有些 \(b_i\) 比较大,会把前面的所有 \(a_i\) 全部消耗,我们称之为断开。所以序列就是一段一段的,前面的消耗量都是 \(a_i\) 的和,最后一段 \(a_i\) 消耗不完,就是 \(b_i\) 的和。
维护每一段的位置很难,但是,其实前面哪些位置断开没影响,我们只关心最后一段是从哪里开始的,因为前面贡献都是 \(a_i\) 嘛,只有最后一段不一样,因为会有些值残留在第n个位置。
结论:设 \(v_i=a_i-b_i\),计算 \(v_i\) 所有后缀和,\(v_i\) 的最大后缀和就是最后的残留量。感觉这个结论挺妙的。
所以用线段树维护 \(v_i\) 的后缀就好了,非常好写,区间加值,询问全局最大。
虽然是个easy version但也是个不错的思考题。hard version不想补了(我是fw)
注:懒得改tex