Loading

CSP-S2019-day2

CSP-S2019-day2

pts: 64

T1: 64

T2: 0/20

T3: 0

T1

[CSP-S2019] Emiya 家今天的饭

数论,数学,容斥,动态规划

solution

32pts: 直接暴力枚举,选哪些食材,并判断某类食材是否超标就好了 代码

48pts: 考虑 \(m = 2\) 的情况,只有两种食材,所以每种食材选的次数必须相等,直接 \(dp\)\(f[i][j][k]\) 表示当时正在决策的烹饪方法,选了 \(j\) 个食材一和 \(k\) 个食材二的方案数代码

64pts: 考虑\(m = 3\) 的情况,和 \(m = 2\) 的情况一个样,

84pts: 对于一种做菜方案,最多只有一种主要食材在超过\(\lfloor\frac{k}{2}\rfloor\)道菜中出现过。因此考虑将答案转化为计算总不合法方案数,用总方案数减去不合法方案数即可

于是便有了一个 \(O(mn^3)\) 的做法:枚举主要食材,\(f[i][j][k]\) 表示前 \(i\) 种烹饪方式中包含\(j\) 道主要食材,有 \(k\) 种烹饪方式没有使用的方案数,\(dp\) 即可。

100pts: 如果 \(t>\lfloor\frac{k}{2}\rfloor\)\(2*t>k\) ,得 \(2*t+(n-k)>n\)

因此可以将原来使用某种主要食材的菜看做使用了两次该食材,并为每种烹饪方式加一种名叫 “不选” 的菜,其使用了所有的主要食材各一次,所有使用某种主要食材大于 \(n\) 次的方案即为不合法方案 代码

T2

[CSP-S2019] 划分

贪心,单调队列

32pts: 最后忘了把调试输出的 \(F\) 删掉因此爆 0/kk 很简单的一个 dp, \(f[i][j]\) 表示到第 \(i\) 个数,上一个次在 \(j\) 点划分的最大方案;

转移: \(f[i][j] = min(f[j][k] + (sum[i] - sum[j]) * (sum[i] - sum[j]))\)

条件:\(sum[i] - sum[j] > sum[j] - sum[k]\)

时间复杂度: \(O(n^3)\)

64pts: 发现 \(k\) 的作用只是判断转移过来的状态合不合法,所以我们可以再开一个数组把这一维滚掉,\(last[i]\) 表示到 \(i\) 到上一个端点 \(j\) 之间的和,\(last\) 可以在转移的时候可以顺便求出来 代码

时间复杂度:\(O(n^2)\)

100pts: 贪心发现段分的越多答案越小,\(f[i]\) 表示到 \(i\) 点前的最后一个端点,显然要使得 \(f[i]\) 尽可能的靠近 \(i\)

\(f_i\) 一定是单调不降的,合法的条件为 \(sum_i - sum_j\geq sum_j - sum_{f_j}\),可以变形为 \(sum_i\geq sum_j+(sum_j-sum_{f_j})\) 根据题意,\(sum_j-sum_{f_j}\)\(sum_j\) 具有单调性。显然,这可以用单调队列来维护 \(f_i\) 的值 代码

复杂度 \(O(n)\)

T3

CSP-S2019] 树的重心

树形 dp

solution

题解原文

关于重心有三个性质

  • 一棵树如果有两个重心,这两个重心一定是相邻的
  • 一棵树的重心一定在根节点所在的重链上
  • 一棵树的重心一定是以该树根节点重儿子为根的子树的重心的祖先

前面两个可以根据重心的性质的出来

根据性质1:我们可以先找到深度较大的重心,然后对于已求出的重心再判断其父亲是否也是重心

根据性质2:我们在找重心的时候可以只往根节点所在的重链上找;

根据性质3,我们可以从下往上找重心,不用每次重新找。

还有一些性质,接下来对于每种情况具体分析。

删掉一条边后一棵树会变成两部分,设该边的两个端点为x,yx,y,其中深度较大的一点为yy,则这两部分分别为以yy为根的子树和整棵树减掉以yy为根的子树。我们先分析以yy为根的子树。

以 y 为根的子树

我们可以根据性质3预处理出以所有节点为根的子树的重心,只要不断向上走就可以,时间复杂度 \(O(n)\)

整棵树减掉以 y 为根的子树

根据性质2,这棵树的重心一定在根节点所在的重链上。试想,只要删掉的以 \(y\) 为根的子树大小一样,位置实际上是对重心没有影响的,唯一有可能影响的情况就是删掉的这个子树在以根节点重儿子为根的子树中。我们将这些情况再具体分析。

y 不在以根节点重儿子为根的子树中

这是最平常的情况。既然只有删掉子树的大小对重心有影响,我们可以预处理出删去所有大小的子树之后的重心,然后直接询问。这个预处理只需要从根节点所在的重链从下到上走一遍就可以,时间复杂度 \(O(n)\)

y 在以根节点重儿子为根的子树中

试想,如果删掉以 \(y\) 为根的子树后,这个重儿子仍然是重儿子,那么重心还是会在原来这条重链上,而重心显然只可能往重链上远离 \(y\) 的一端移动,而如果根节点就是重心,删掉子树之后根节点仍然会是重心。因此,最开始我们可以让整棵树的重心作为根节点,处理这种情况就变得很方便。

如果删掉子树后这个重儿子不再是重儿子了怎么办?显然,现在这个重儿子只会是原来的次重儿子。因此,我们可以预处理出次重儿子所在的重链上删去所有大小的子树之后的重心,然后按照 \(y\) 不在以根节点重儿子为根的子树中的情况处理。

所有预处理都是 \(O(n)\),每次询问 \(O(1)\) 总的复杂度 \(O(n)\) 代码

posted @ 2021-05-30 10:55  Dita  阅读(80)  评论(0)    收藏  举报