【题解】牛客CSP-S提高组赛前集训营6
A. 集合统计
出题人:henry_y
题意
定义一个集合 的
函数为
。给定一个集合
,求该集合所有非空子集的
函数之和。
题解
解法一(
)
直接枚举所有子集判断即可。复杂度 。
解法二(题解做法)
考虑每个数作为答案的贡献。
设小于 的数共有
个,那么
作为最大值的子集共有
个,对答案累加上
即可。
作为最小值同理。
找到小于 的数和大于
的数可以直接排序。
复杂度
标程
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=41720323
B. 下雨天
出题人:Trote
题意
有 个池塘,第
个池塘容量为
,初始时水量为
。接下来
天会发生水量变化:设第
个水池当前水量为
,经历了某天,水量如果增加
,那么该水池在这天过后的水量为
;若
是个负数(水量减少),那么水量为
。求每天所有水池的总水量。
题解
解法一(
)
注意到 的数据有
分,可以直接暴力获得。
出人意料的是第 个点
,测试中某些暴力也能跑过,这样暴力可以获得
分。
解法二(数据随机)
接下来需要稍微认真一些地分析问题。
首先注意到小的池塘的水量一定小于等于大的池塘。
然后注意到大小相邻的两个池塘水量要么相等,要么水量相差 。
或者说,一段相邻的关系要么呈斜率为 的斜线,要么呈斜率为
的直线。
注意到至少有 的数据随机,观察后发现随机情况下,斜线和直线的数目较小,可以用预先编号分段后利用线段树或者其他方法处理。
这样大概可以获得 分左右,会被一些精心构造的数据卡掉。
解法三(题解做法)
可以发现,相邻关系不同的数目是 的,每次最多生成两个新的,或至多删除所有旧的。
利用一个栈维护即可获得满分。
均摊时间复杂度 ,当然也可以用
之类的东西维护。
想起来总觉得很难写,其实写了之后发现没有那么困难。
标程
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=41725379
C. 树上队列
出题人:Tweetuzki
题意
一棵 个点的树,求多少种 DFS 序满足任意相邻
个节点的导出子图连通。求所有
都求一遍答案。
题解
解法一(
)
暴搜每种合法的 DFS 序,检验是否合法。注意一下实现应该可以通过 以内的分数。
解法二(树退化成链)
我们发现不论 取何值,只有从两端中的一端走到另一端合法,也就是所有的答案都是
。因此输出
个
即可。
解法三(
)
有一个非常重要的性质隐藏在题目里:。
那么我们因此有一个结论:在开始时,队列一定踞在原树的某一棵子树里,且去掉队列所占有的这些节点后,仍然是一个连通图。
这是因为既然 ,就意味着队列开始时占据的
个节点在移动过程中必须移开。而一旦这些节点将原树分成两个子树,那么当队列全部移开时,就会导致顾此失彼,这些子树没有边将它们合并起来。也就是说这样就会导致队列内元素不连通,这是一种不合法的方案。
形式化地说,就是:
令 表示
是否在初始队列中,即
初始队列
,
为边集。那么一个合法方案的初始队列就一定有且仅有一个
,使得
。
根据对称性,可以发现最终队列一定也是踞在一个子树中的。
我们先介绍一个 的做法:考虑枚举队列所在子树的根节点
。一个节点
可以唯一确定一个初始时的队列(它确定了队列的大小、所占格子,但无法确定顺序,这是我们之后需要统计的)。这是因为我们假设
的父亲是
,则只有
这一条不属于该队列。如果我们将
设为全树的根,那么
的大小就是
。由于
,因此
一定是
所有子树中唯一一个最大的。
于是我们就可以确定哪些节点是初始队列中的节点,哪些节点不是队列中的节点。
接下来我们讨论情况是否合法。
情况合法的充要条件是,在 子树的所有节点中,儿子节点个数大于
的节点的子树大小不超过
。这个是很好理解的,因为队列遇到分叉地方必须要分开,而这个子树的根节点是无法移开的(不然就把这个叉分开了),因此所有儿子节点个数大于
(有分叉)的子树大小在最终状态中肯定是被队列占满的,否则不合法。
在合法的状态中,我们需要统计方案数。
根据乘法原理我们发现,方案数就是 子树的拓扑序数量乘上
子树除去
子树后的拓扑序数量。拓扑序的数量可以通过预处理组合数,然后依次合并子树转移。
时间复杂度 。
解法四(题解做法)
不管你是什么方法得到的反正我是打表发现的一个结论:对于答案序列 ,一定存在一个划分点
,满足对于
的答案都是
,
的答案都为一个相同的值。
这个结论的正确性是很容易理解的。根据上面的 做法我们发现,队列的路线一定是从一个子树出发,然后经过一条长长的链,最后落到一棵子树中。再根据已经提及的合法条件可知,随着队列大小的扩大,知道扩大到这两端较大的子树的大小后,就能够填满这两棵子树,开始出现合法方案,而且在这之后随着队列大小的增加,新的元素可以被挤到中间那一条链上去,而这样是不会影响总方案数的。
因此我们试图在原图上找到这样一个类似于流星锤的形状:两棵子树 ,满足较大的子树大小尽可能小,以及中间的一条长链,这条长链上有所有不在
子树内的点。注意这里的长链指的是边。
想到子树大小尽可能小,我们自然地想到了树的重心(PS:比赛时,选手 Freopen 提交了一个这么找重心的方法——先随便以一个点为根,然后找到树上唯一一个子树大小为 的点,即为树的重心。在这道题上,这种方法是正确的。因为如果不存在这样一个点,那么答案一定全部都是
)。我们可以找出树的重心
,然后找到重心的最大子树
,可以发现一旦存在答案,那么
的这条边一定是“流星锤”中间这条链的一部分。然后把
这条边不断往外延伸,就可以找到“流星锤”两端的子树节点
。然后这个划分点
就等于
子树大小的较大值,答案就是
子树的拓扑序数量乘上
子树的拓扑序数量的两倍。
预处理阶乘的逆元 ,找重心
,动态规划求拓扑序
,输出答案
,因此总复杂度
。
解法五(验题人做法)
对解法三的 DP 进行换根优化。注意细节。
时间复杂度也是 。
标程
解法四
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=41724753
优化找重心:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=41724987
解法五
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=41724763