DP专项
1st 跳蚤电话
倒着考虑操作,就是每次删去一个一度点,或者将一个二度点与它连接的两条边合为一条边。计算方案数要乘很多组合数不大方便,可以先计算概率,最后再乘总方案数。
设 \(i\) 的父亲不可被删, \(i\) 子树能删空的概率为 \(f_i\) 。转移时讨论 \(i\) 如何被删去。
若 \(i\) 最后被删,则作为一度点被删去,贡献的概率是 \(\prod\limits_{j\in son_i}f_j\) 。
若 \(i\) 作为二度点被删去,则枚举最后被删的点 \(j\) 。那么 \(j\) 子树的贡献为 \(\prod\limits_{k\in son_j}f_k\) , \(j\) 子树外的贡献,考虑 \(i\) 到 \(j\) 的路径上的点,它们一定都作为二度点被删去,且它们不在路径上的子树都提前被删空。设路径上的点从 \(i\) 到 \(j\) 依次为 \(a_1,a_2,\ldots,a_k\) ,那么这部分贡献就是 \(\prod\limits_{u=1}^{k-1}\frac{1}{siz_{a_u}-siz_{a_{u+1}}}\prod\limits_{v\in son_{a_u}\& v\neq a_{u+1}}f_v\) 。最后因为枚举最后删的点,再乘上 \(\frac{1}{siz_i}\) 。综上直接计算是 \(O(n^2)\) 的。
这样其实有很多重复计算。每次从儿子到父亲, \(a\) 只会在开头处增加一个父亲。因此可以设 \(g_i=f_i\times siz_i\) ,有
复杂度 \(O(n\log n)\) 。
\(\color{teal}{click\;for\;the\;code}\)
2nd 密码锁
图 \((V,E)\) 是一个竞赛图,那么缩成的DAG会呈链状,每个点向它之后的所有点连边。要求DAG上的点数,可以转化为链上的前缀数。
定义割集为点集 \(S\) ,满足 \(S\) 中的点与 \(V-S\) 中的点之间的有向边都从 \(S\) 指向 \(V-S\) ,那么DAG链上的前缀数就是割集数量。
对于一个点集,它是割集的概率就是其中的点每条边连出去的概率乘积。由于概率不为 \(\frac{1}{2}\) 的边较少,我们可以单独考虑这些特殊边的额外贡献。设它的概率为 \(p\) ,那么这条边的贡献就是 \(2p\) ,最后统计大小为 \(siz\) 的连通块中特殊边的总贡献,再统一乘 \((\frac{1}{2})^{siz(n-siz)}\) 。单独算出连通块贡献后做一遍背包即可得到连通块总贡献。
单独考虑特殊边构成的连通块, \(m\) 条边最多有 \(m+1\) 个点,暴力枚举哪些点在割集中即可。复杂度 \(O(2^{m+1}n+n^2)\) 。
\(\color{teal}{click\;for\;the\;code}\)
3rd 滑稽树上滑稽果
感谢 \(z\color{red}{ero4338}\) 指正/kk
观察思考后可以得到,最终最优方案一定是一条链。于是就把树形问题转为了序列问题。
把所有 \(a\) 相同的二进制位提出来,这部分的贡献一直不变。其他部分考虑DP,可以设 \(f_{S}\) 为将 \(S\) 与成 \(0\) 需要的最小代价。这样转移是 \(O(an)\) 的。
继续优化,每次转移枚举子集而不是 \(a\) 。这样需要预处理对于每个二进制数 \(T\) ,是否存在一个 \(a\) 使 \(t\& a=0\) 。这个可以直接 \(g_{T|2^j}|=g_T\) 转移。于是复杂度变成 \(O(a^{\log_23})\) 。
\(\color{teal}{click\;for\;the\;code}\)
4th 梦中的题面
前60分 \(c=1\) 就是 \(b\) 进制下的数位DP。先背包预处理出 \(g_{i,j}\) 表示 \(i\) 个属于 \([0,b-1]\) 的数拼出 \(j\) 的方案数,然后设 \(f_{p,d,t}\) 表示从低到高DP到 第 \(p\) 位,当前有 \(d\) 个进位, \(t\) 表示 前 \(p\) 位是否小于 \(n\) 的前 \(p\) 位。用 \(g\) 辅助转移很简单。
\(c=0\) 时多了在前面所有位选 \(0\) 并在当前位贡献一个进位(即在当前位选 \(b\) )的选择,,可以给 \(f\) 加一维状态 \(z\) ,表示当前位强制 \(z\) 个数必须选 \(0\) 。在最低位枚举 \(z\in[0,m]\) ,即枚举在某一位选 \(b\) 的数的数量。转移时如果选 \(b\) ,那么它这个数在这位之前之后都强制选 \(0\) , \(z\) 不变;否则这个数在这位之后强制选 \(0\) , \(z\) 加一。复杂度 \(O(m^3b^2)\) 。
\(\color{teal}{click\;for\;the\;code}\)
5th Make It Ascending
给出的操作相当于将序列划分为若干个集合,要求其中在每个集合中选出一个代表点,按代表点位置排序后集合内数值之和单调递增。
先不考虑位置,考虑排除重复集合。我们可以通过按权值递增进行DP实现这一点。设 \(f_{cnt,sta,val}\) 表示把 \(sta\) 中的元素划分为 \(cnt\) 个集合,最大权值为 \(val\) 是否可行。贪心地想, \(val\) 越小肯定越优,于是把一位状态移到DP值, \(f_{cnt,sta}\) 表示把 \(sta\) 中的元素划分为 \(cnt\) 个集合,最小的可能最大权值。可以通过枚举子集做到 \(O(n3^n)\) 。
再考虑位置问题。上面的DP已经按权值递增考虑,我们只需让代表点位置同时递增。加一维状态,设 \(f_{cnt,sta,pos}\) 表示把 \(sta\) 中的元素划分为 \(cnt\) 个集合,最靠右的代表点位置为 \(pos\) ,最小的可能最大权值。
仍然贪心, \(pos\) 越小越好,因此不用枚举转移的 \(pos'\) ,直接取枚举子集中第一个大于 \(pos\) 且为 \(1\) 的位置转移。复杂度 \(O(n^23^n)\) 。
DP的同时记录转移前驱即可输出方案。
\(\color{teal}{click\;for\;the\;code}\)
6th turtle
大力发掘性质。
首先最后的方案中第一行肯定递增,第二行肯定递减。设第一行第 \(i\) 个元素为 \(a_i\) ,第二行第 \(i\) 个元素为 \(b_i\) , \(a_{i+1}-b_i=d_i\) ,在第一行向下转的权值为 \(val\) ,那么在第一行第 \(i\) 位向下转的权值即为
因为 \(a_i\) 递增, \(b_i\) 递减,所以 \(w_i\) 递增,最大权值肯定在第 \(1\) 位或第 \(n\) 位转向时取到。而第一行第一位和第二行第 \(n\) 位是一定要走的,所以把最小值和次小值放在这两个位置一定不劣。那么问题就变成在 \(2n-2\) 个数中选 \(n-1\) 个数,令这 \(n-1\) 个数加和为 \(sum\) , \(2n-2\) 个数加和为 \(all\) ,使 \(\max(sum,all-sum)\) 最小。做背包,找到可达的第一个大于等于(或最后一个小于等于) \(\frac{all}{2}\) 的值即可。 复杂度 \(O(n^2\sum a)\) 。
同样DP时记录前驱。(被卡常被迫特判了全是50000的情况
\(\color{teal}{click\;for\;the\;code}\)