2020.11.17提高组模拟
2020.11.17【NOIP提高A组】
码 \(code\) 太投入忘记交程序了呜~
T1. 数数(cuvelia)
可以通过归纳证明:当长度 \(i\) 为偶数时,最优方案一定是原序列排序后前 \(\frac{i}{2}\) 个和后 \(\frac{i}{2}\) 个。
进一步可以发现,偶数的最优方案加上剩下数中的任意一个贡献相同。
于是就这么做完啦。
T2. 数树(voltississimo)
令 选择一条边 表示令一条边 不合法。
设 \(G(i)\) 表示至少选择 \(i\) 条边的方案,那么只需要求出 \(G(i)\),简单容斥可以得出不合法的方案数,用总方案数 \(n!\) 减去即可以求出答案。
考虑求 \(G(i)\),首先可以发现,对于点 \(u\),其最多只能分别成为一次被选择边终点和起点,那么我们可以设状态 \(f_{u, i, s}\) 表示以 \(u\) 为根的子树中,选择 \(i\) 条边,\(u\) 的状态为 \(s \in\text{{0, 1, 2, 3}}\) 的方案数,注意 \(f\) 并不包括给每个点分配具体的数字的方案,其仅仅是选边的方案数。
转移也比较简单:
其中 \(w(u, v)\) 表示边 \((u, v)\) 的状态。
最后 \(G(i) = \sum{f_{1, i, s} \times (n - i)!}\),\((n - i)!\) 为给每个点分配具体数字的方案数,注意每选择一条边,就有两个点被捆绑(即确定其中一个的数,另一个以唯一确定),考虑一开始将每个数看成一块,那么选择一条边就会将两块捆绑在一起,所以最后共有 \((n - i)\) 个块。
T3. 鼠树(pastel)
修改操作比较烦(于我而言,就是打了两次 \(6.5k+\) 的 \(code\) 到最后发现操作 \(6\) 没法维护( ̄﹏ ̄;)),所以先考虑不带修改的情况:
对于操作 \(2\),直接修改被修改点的权值比较困难,所以考虑在对应的黑点上打上标记;
那么操作 \(1\) 直接找到对应的黑点即可;
操作 \(3\) 比较复杂,首先我们需要求出子树内 \(\sum{v_iw_i}\) (\(v_i\) 为黑点的权值,\(w_i\) 为黑点管辖的白点数),然后询问点下白点数及管辖询问点的黑点的 \(v_i\),最后答案为 \(\sum{v_iw_i}\) + 询问点下白点数 \(\times\) 管辖点 \(v_i\);
将原树 \(DFS\) 序重编号后,操作 \(4\) 相当于给一段连续区间内对应黑点加权。
综上,我们需要维护 \(v_i\)(单点查 + 区间改 \(w_i > 0\)),\(w_i\) (单点查 + 修改),\(\sum{v_iw_i}\),此部分可以用线段树维护;以及找到对应的黑点,可以树剖 + \(set\) 维护。
有了以上基础,再考虑带修改的情况:
- 操作 \(5\)(白变黑),只需要找到待修白点对应的黑点,继承其 \(v_i\),修改两点 \(w_i\) 即可
- 操作 \(6\)(黑变白),设待修点为 \(x\),\(x\) 祖先中最近的黑点为 \(y\),首先给子树 \(u\) 中所有点加上 \(v_x - v_y\),再用操作 \(4\) 给 \(u\) 子树中黑点加上 \(- (v_x - v_y)\) 的权值(结合操作 \(1\) 理解),同时修改 \(w_x, w_y\)
在第一部分的基础上需要维护每个点的权值(操作 \(6\))并支持区间修改查询,同样一棵线段树维护即可(树状数组也行)。
十分锻炼码力的一道题(其实如果打的是正解的话也不算复杂,只是 于我而言 比较长而已)