2023.4.24 nfls集训Day6总结

(从这篇开始只更Div1了)

link

感觉IOI赛制与OI赛制差别太大,时间安排可能没什么参考?暂时不记录了(实际是太懒了)。

T1:

简单数数题,数 \(n\) 维超立方体中至少到三个定点某一个的最短路小于等于给定的值的点数。

首先观察出两点间距离是两个点所代表二进制数的不同位个数,这个不难证明。

那么就可以写 \(O(2^n*n)\) 的暴力了,可以拿20pts。

IOI赛制提交完发现有分,那么结论就是对的,继续推式子,发现这个东西可以容斥,到一个点的,同时到两个点的,同时到三个点的,前两种可以预处理组合数每行前缀和做到 \(O(1)\)\(O(n)\),预处理是 \(O(n^2)\),第三种需要枚举四维,一维可以用组合数前缀和优化,但还是 \(O(n^3)\) 的。

但是剪枝卡常就过了,虽然赛后被自己hack了,复杂度 \(O(n^3*?)\)

正解是计算补集,变成对三个点都不满足要求的点数,枚举其中两个变量,列出来另外个变量对应的约束条件,发现可以写成组合数矩形和的形式,预处理二维前缀和就好,复杂度 \(O(n^2)\)

T2:

性质DP题。

不妨只考虑B的匹配,B匹配完A必定也匹配完了。

当B比较小的时候,可以全排列枚举 S 中的 B 具体匹配 T 中的哪个 B,匹配的代价需要用到两个 B 的距离 \(d\),当 A 和 B 的权重相等时,不需要考虑移动时的区别,只需要 \(d\) 的奇偶性看是否需要特殊的进行一次两个数的交换。

否则权重不同时,经过 A 的数量越多越好,不难想答案下界是 A 和 B 权重为1时的答案+匹配排列的逆序对数,通过打表+感性理解发现永远能达到下界。

那么就需要计算用什么样的匹配排列,不难发现把 T 中的 B 按下标奇偶分类,下一个匹配的 S 中的 B 一定是选择在当前奇偶性的下标集合中最小的那个,换句话说就是匹配排列按奇偶分成两个递增序列。

那么就可以设计DP,令 \(f_{i,j}\) 表示 S 中前 \(i\) 个 B 匹配了 \(j\) 个奇数下标,那么偶数下标就有 \(i-j\) 个,状态数 \(n^2\),转移 \(O(1)\),复杂度 \(O(n^2)\)

T3:

细节非常多的计数DP。

容易发现路径一定是形如每一层分成了若干线段,每个线段伸出两个插头,下一层继承这些插头,与这一层的单点合并,在最后一层所有线段合并成1个,并且路径满足切到下一层前之前层的所有点都必须已经包含在路径内,这些性质非常好。

\(f_{k,i}\) 表示第 \(k\) 层有 \(i\) 个线段的方案数。

那么下一层需要先继承 \(2i\) 个插头,可用的单点数量是 \(n-2i\),我们需要将这些单点看做伸出的两个插头都是自己的线段,这样的线段是不能延伸到下一层的,所以走到下一层不能存在单点。

直接算很难做,考虑容斥,枚举合并其中的 \(c\) 个线段,单点的数量是 \(k=n-2i-c\),枚举下一层要变成 \(j\) 个线段,那么需要将这 \(i+c\) 个线段合并成 \(j-k\) 个再加入 \(k\) 个单点,容斥系数根据二项式反演理论不难推出是 \((-1)^k\)

于是要求出将 \(i\) 个线段合并成 \(j\) 个的方案数,因为线段间无序,所以先乘上 \(i!\) 定序,然后把 \(i\) 个元素划分成 \(j\) 个非空集合,即 \(C_{i-1}^{j-1}\),最后除以 \(j!\) 消序。

初始状态就是枚举所有排列,划分为 \(i\) 个线段,然后分类,具体的就是 \(\frac{n!C_{n-i-1}^{i-1}}{i!}\)

最后合并相当于 \(i\) 个线段成环的方案数,即乘上系数 \(\frac{(n-i)!}{n-i}=(n-i-1)!\)

这样做 \(m\) 要提前-2,因为第一层和最后一层是特殊考虑的。

直接做是 \(O(m{\frac{n}{2}}^3)\) 的,中间的转移都一样,考虑矩乘优化,不能完全倍增,那么考虑倍增 \(t\) 次,复杂度 \(O(t{\frac{n}{2}}^3+\frac{m}{2^t}{\frac{n}{2}}^2)\)\(t=4\) 就足够通过了。

T4:

交互背景的树剖+分类讨论。

链是显然的,倒序从深度第二大的点往上填就好,操作刚好是 \(n-2\) 次。

树有一个询问次数 \(O(n^2)\) 的暴力,枚举遍所有点对,这样可以求出每个点的子树点集和到根的路径集合,求出这些后这题随便做。

考虑任意一个加入点的顺序,在加入当前点之前维护之前已知点构成的树形态(换句话说就是虚树)。

考虑当前的虚树 T ,从根节点1考虑,假如现在在点 \(x\),他的虚树上子节点集合是 \(e_x\),当前要加入的点是 \(cur\)

如果没有孩子,那么就把 \(cur\) 加入 \(e_x\),否则先查询一下 \(e_x\)\(cur\),得到 \(z\)

接下来分类讨论,假如 \(z=x\),那么还是直接把 \(cur\) 加入 \(e_x\)

否则如果是 \(e_x\) 中的某个点,那么递归做下去。

否则如果是 \(cur\) 或者某个虚树上不存在的点,那么通过集合二分就可以知道具体在哪个儿子的分支上,那么在 \(x\) 与这个儿子之间插入 \(z\), 再看 \(z\) 不是 \(cur\) 就把 \(cur\) 加入 \(e_z\)

这样子询问次数大概是 \(O(n^2)\) 的(还不如暴力好写),但是这个东西是可以树剖优化的,具体的如果需要递归的是轻儿子就递归下去,否则就直接在重链上查询,如果查链底和 \(cur\) 得到的点是已经存在的点就递归下去(本质还是切换重链),否则就可以直接二分重链找到答案,询问次数 \(O(nlogn)\),树剖与二分独立。

posted @ 2023-04-24 21:15  Displace  阅读(59)  评论(0)    收藏  举报