vjudge DP
uoj607 跳蚤电话
正着不好做(还需要考虑非树边),但倒着就变成了每次在树上 删去一个一度点 或 删去一个二度点并合并邻点
树上一般按子树考虑,直接算方案数的话需要合并子树,因此考虑先算概率,这样每个子树就独立了。
设 \(f[i]\) 为随机一个该子树中点的排列为合法删点顺序(不准删 \(fa[i]\))的概率,\(s[i]=\prod_{j\in\text{son}(i)}f[j]\) 。答案为 \((n-1)!s[1]\),转移考虑枚举最后一个被删的点:
- 为 \(i\):\(s[i]\)
- 为 \(j\ne i\):分子树 \(j\) 和其他两部分。第一部分显然为 \(s[j]\);令 \(a[1]=i,\cdots,a[k]=j\) 为 \(i\) 到 \(j\) 路径上的点,不难发现 $ a[k],1\le k<j$ 一定是作为二度点被按顺序删掉,这部分概率为 \(\displaystyle\prod_{l=1}^{k-1}\frac{1}{siz[a[l]]-siz[a[l+1]]}\prod_{u\in\text{son}(a[l]),u\ne a[l+1]}f[u]\)(乘的分数为子树 \(a[l]\) 中除子树 \(a[l+1]\) 外的点都要在 \(a[l+1]\) 前被删)
由于转移中钦定了最后被删的点,还要乘上 \(\frac{1}{siz[i]}\)。朴素实现 \(O(n\sum dep^{2})\),直接考虑子树中每个点作为 \(a[l]\) 的贡献可以做到 \(O(n\sum siz)\)
进一步观察,对于 \(a[l],a[l]\in\text{subtree}(j),j\in\text{son}(i)\),\(a[l]\) 对 \(f[i]\) 贡献与对 \(f[j]\) 贡献的区别仅为在 \(a\) 序列开头加了一个 \(i\),可以优化掉。设 \(g[i]=f[i]\cdot siz[i]\),则有 \(\displaystyle g[i]=s[i]+\sum_{j\in\text{son}(i)}g[j]\frac{1}{siz[i]-siz[j]}\prod_{k\in\text{son}(i),k\ne j}f[k]\),时间复杂度 \(O(n)\)
官方题解的算法六把 DP 式子展开能得到非常简单的形式,感觉意义不大就鸽了
uoj181 密码锁
根据竞赛图的性质,SCC 的个数等于缩点后链的前缀数。设这样的前缀为点集 \(S\),等价于求满足 \(S\) 与 \(V-S\) 的连边都是从 \(S\) 连出去的概率之和,暴力枚举 \(S\) \(O(2^{n}m)\)。这步转化非常关键,它让答案的计算方式从图的形态相关弱化为了点集相关
这种题(仅有给定的边权与其他不同)的一个经典套路是只考虑特殊边,统计每个 \(|S|\) 的答案,然后乘上 \(0.5^{|S|(n-|S|)}\)(为了抵消一个 \(0.5\),特殊边的概率应 \(\times2\))。直接想法是对特殊边形成的联通块枚举有贡献的边集,要求这些边构成二分图,那么一定有一部点 \(\subseteq S\),做一个类似背包的 DP 即可(\(f[i]\) 表示 \(|S|=i\) 且 \(S\) 与 \(V-S\) 间的特殊边都是从 \(S\) 连出去的概率之和)。博主并不太懂这种做法,但官方题解说复杂度为 \(O(2^{m}n^{2})\)
考虑一种比较好的实现:对每个联通块枚举 \(\subseteq S\) 的点集,\(O(m)\) 判断一条特殊边是否有贡献,同样做背包,时间复杂度 \(O(2^{m+1}n+n^{2})\)(这样的联通块大小上界为 \(m+1\))
uoj370 滑稽树上滑稽果
题目加粗了“滑稽度”和“大小”,然而还是读错题了,不愧是星际玩家
方便起见,把所有数都为 \(1\) 的二进制位计入答案,然后置为 \(0\),现在所有 \(a\) 的 &
为 \(0\)
根据 sub2 冷静一下,发现答案一定是一条链(从根到叶子滑稽度是不增的),因此我们希望滑稽度尽快到达 \(0\),一个贪心是从小到大拍,根据 sub4 再冷静一下,这个贪心是假的
考虑 DP。设 \(f[s]\) 为链尾的滑稽度,转移枚举下一个数 \(a_i\),则 ckmin(f[s&a[i]], f[s]+(s&a[i]))
。看上去 \(a_i\) 可能被重复选择,不过由于 &
性质,选选过的 \(a_i\) 会得到 \(s\&a_{i}=s\),因此不用担心,时间复杂度 \(O(an)\)。
进一步思考,枚举 \(a_i\) 实质上是要得到 s&a[i]
来转移,那么我们可以直接枚举 \(s\) 的子集 \(t\) 转移到 s^t
(把 \(s\) 中 \(t\) 包含的位 置为 \(0\)),该转移合法的条件是存在 \(a_i\) 使得 (t&~a[i])==t
,容易预处理
复杂度为 \(O(3^{\log_{2}a})\),需要 0.8s,不过加上用贪心结果来剪枝就很快了
uoj312 梦中的题面
吐槽一下官方题解,写得真的烂。看了一晚上没懂算法六
先考虑 \(c=1\),根据 \(b^i\) 不难想到 \(b\) 进制下从低到高 DP。对于 \(b^i\) 这一位,有 \(n-i\) 个数可以任填 \([0,b)\),预处理 \(f[i,j]\) 表示 \(i\) 个取值 \([0,b)\) 的数和为 \(j\) 的方案数。设 \(g[i,j]\) 表示该填 \(b^i\) 位,\(\sum x-n\) 的进位为 \(j\)( \(j\) 取值 \([-1,m)\)),枚举 \(\sum x\) 这一位的和即可转移,复杂度 \(O(m^{3}b)\)
\(c=0\) 需要特殊考虑 \(=b^{i}\) 的数,在上面的 DP 中相当于低位全为 \(0\),直接向 \(b^{i}\) 进一位。考虑加一维 \(k\) 表示剩下的 \(n-i\) 个数中有 \(k\) 个 \(\ne b^{i}\),因此当前位有 \(k\) 个数不为 \(0\)。然后讨论 \(x_i\) 是否等于 \(b^{i}\) 对应转移即可
代码实现上有一些细节,并要注意常数
还有不依赖 \(b\) 的容斥做法,见『模拟赛 3.20 』
CF1342F Make It Ascending
第一道自己想出来的题,虽然比正解多了一个 \(n\)。看这个数据范围就像 \(O(T3^{n}\text{poly }n)\),并且这个 7s 恐怕可以无视 \(\text{poly }n\)
显然操作数最少等价于剩下的数最多,考虑状压 DP 结果序列。设 \(f[i,s,j]\) 为以 \(i\) 结尾的序列,\(s\) 集合中的数或被操作或剩下,末尾数值为 \(j\) 最多能剩下多少数。发现 \(f[i,s]\) 取值范围只有 \(n\),且 \(f[i,s,j]\) 相同时只有最小的 \(j\) 有用,可以反转状态和 DP 值,改设 \(f[i,j,s]\) 为剩下 \(i\) 个数,末尾数在原序列 \(j\) 位置,使用集合 \(s\) 的末尾数最小值。转移就枚举新的末尾数位置 \(k\) 和加给 \(k\) 的集合 \(t\subseteq U-s\),输出方案就记录转移。时间复杂度 \(O(T3^{n}n^{3})\) 不满
其实也容易优化掉一个 \(n\),发现转移时若 \(i,j,s,t\) 一定时只有最小的 \(k\) 有用。另外这题有很多做法
CF1239E Turtle
根据题解理解不难,但如何想到?
直接做很难,先找一些性质:
- 第一行第一个数、第二行最后一个数是最小的两个数,因为所有路径都经过这两点。结合第二条反证也可
- 第一行的数单增,第二行单减:路径和为 第一行前缀 \(+\) 第二行后缀 的形式,调换第一行的逆序对 \(i,j\),对于 \(\ge j\) 的前缀无影响,但 \(<j\) 的前缀变小了。第二行类似
- 考虑拐点从 \(i\) 变到 \(i+1\) 的增量 \(a_{1,i+1}-a_{2,i}\),根据第二条不难发现单增,因此取到最大值的路径拐点为 \(1,n\)(增量初值可能 \(<0\)),即答案为 \(\max(a_{1,1}+\sum a_{2},\sum a_{1}+a_{2,n})\)
最后问题转化为把较大的 \(2n-2\) 个数分成两部分(每部分 \(n-1\) 个数),使较大的和最小。bitset
背包即可做到时空 \(O(\frac{n^{3}a}{\omega})\)(为了输出方案不能滚动)
CF613E Puzzle Lover
模拟赛做过,但仍调了很久,没救了。做法略
需要判断 \(s,t\) 两子串是否同构,当时的做法是哈希。但实际上可以转化为求两后缀 LCP,瓶颈在于 DP 因此直接 \(O(|s||w|)\) 递推即可,代码阳间许多
DP 部分,计数的不重不漏其实是很严谨的。把路径分成三部分:第一部分先向左再拐回来,第二部分单调向右,第三部分先向右再拐回来。第一部分枚举向左的开始位置和长度(\(\ge1\),特判掉单个位置与 \(w_1\) 匹配)作为 DP 初值,第二部分 DP,从上一列转移,第三部分枚举向右的开始位置和长度(\(=0\text{ or }>1\),\(=1\) 的情况包含在第二部分中)统计答案
考虑哪些串被漏掉:第二部分向左,或仅有第三部分。reverse
\(s\) 再做一遍即可(注意不能 reverse
\(w\))
CF582D Number of Binominal Coefficients
以前口胡过,今天一写果然很恶心
库莫尔定理转化为求 \(0\le x,y\le A,x+y\le A\) 且 \(x+y\) 进位次数 \(\ge\alpha\) 的解的个数,数位 DP 即可。口胡五分钟,调试两小时
以 \(p\le x+y<p+a\) 为例推一下式子:
AGC34E Complete Compress
因为细节 WA 了一次,还是看了题解
AGC20E Encoding Subsets
先考虑 \(1\) 不能变成 \(0\):区间 DP。设 \(f[l,r]\) 为区间 \([l,r]\) 的答案,转移枚举能折叠成一个字符的后缀长度,\(g[l,r]\) 为 \([l,r]\) 折叠成一个字符的方案数,转移枚举 \(|A|\)。原题先将记区间改为记串,转移 \(g[s]\) 时要乘上 \(f[\text{ 截取出的 }\frac{|s|}{|A|}\text{ 段的按位与 }]\)。这样会产生不是原串子串的串,记搜即可
关键在于计算复杂度,官方题解给了三种方法:
- 程序员:打表发现需要计算的串只有 \(49893\) ,转移需要 \(O(n^{2})\),二者都不满
- 数学:长度 \(\le12\) 的串只有 \(2^{13}\) 种,而 \(12\times8>100\),因此要产生 \(>12\) 的串只能折叠两次(每次折叠长度至少减半),折叠出的子串可以表示成 \(s[i:i+k-1],s[i+k,i+2k-1],s[j:j+k-1],s[j+k,j+2k-1]\) 的按位与,只有 \(O(n^{3})\) 种,再加上 \(n^{2}\) 转移,总复杂度为 \(O(2^{\frac{n}{8}}n^{2}+n^{5})\) 不满
- 信仰:写出来跑一下发现 \(O(\text{能过})\)
这题带来一个启示就是数据范围小时一些奇怪复杂度的东西可能能过,不要直接放弃
AGC36D Negative Cycle
没有负环等价于差分约束有解,设最终取值为 \(x_i,d_{i}=x_{i}-x_{i+1}\),那么 \(x_{i}\ge x_{i+1}\Rightarrow d_{i}\ge0\),存在边 \((i,j,-1)\) 等价于 \(x_{i}-1\ge x_{j}\Rightarrow\sum_{k=i}^{j-1}d_{k}\ge1\),为了使答案最小,边能保留就保留,因此当且仅当 \(\sum_{k=i}^{j-1}d_{k}=0\) 时才会删除边 \((i,j,-1)\)。边权为 \(1\) 的边同理
进一步思考发现 \(q_{i}\le1\),因为把 \(>1\) 的变为 \(1\) 不会影响边权为 \(-1\) 的边,但会少删一些边权为 \(1\) 的边。然后就可以 DP 了,设 \(f[i,j]\) 为 \(q\) 最后两个 \(=1\) 的位置为 \(i,j\),枚举上一个 \(=1\) 的位置 \(k\),二维前缀和一下就能 \(O(1)\) 转移,时间复杂度 \(O(n^{3})\)
AGC28D Chords
一个重要转化是圆上连线相交等价于序列上相交但不包含,这样就可以用区间 \([l,r]\) 表示一个连通块(不存在从区间内连出去的边,\(l,r\) 连通但不一定与区间内所有点连通)。设 \(c(l,r)\) 表示 \([l,r]\) 有多少点没有连边,\(g(n)\) 表示 \(n\) 个点随便连边的方案数,显然 \(n\) 为偶数时\(=\prod_{2i+1\le n}(2i+1)\),否则 \(=0\)。
考虑分每个连通块算。设 \(f[i,j]\) 为存在连通块 \([i,j]\) 时区间 \([i,j]\) 的连边方案数,答案为 \(\sum_{i\le j}f[i,j]\times g(i-1+n-j)\)。计算 \(f\) 可以容斥:\(\displaystyle f[i,j]=g(c(i,j))-\sum_{k=i+1}^{j-1}f[i,k]\times g(c(k+1,j))\)