codeforces杂题

代码力量

基本上还是 dp + 计数。

更多题在这里。

Non-equal Neighbours

\(dp_{i, j}\) 表示第 \(i\) 个位置选 \(j\) 的方案数,那么显然有容斥「全部 - 错误」的转移:

对于 \(j \le a_{i - 1}\)

\[dp_{i, 1 \le j \le a_i} = \sum_{1 \le k \le a_{i - 1}} dp_{i - 1, k} - dp_{i - 1, j} \]

对于 \(j > a_{i - 1}\)

\[dp_{i, 1 \le j \le a_i} = \sum_{1 \le k \le a_{i - 1}} dp_{i - 1, k} \]

我们假设维护一个序列 \(f\) 表示取到当前的 \(i\) 时的 \(dp\),即 \(f_j = dp_{i, j}\),那么我们注意到其实转移等价于:

1.求和 \(sum = \sum_{1 \le i \le a_{i - 1}} f_i\)

2.将 \(1 \le i \le a_{i - 1}\)\(f_i\) 乘上 \(-1\)

3.将 \(1 \le i \le a_i\)\(f_i\) 加上 \(sum\)

4.将 \(i > a_i\)\(f_i\) 设为 \(0\),也就是乘上 \(0\)

注意到这正好契合线段树的区间修改操作,显然弄个动态开点的线段树就做完了。

code

Path Counting

我不会告诉你我想了很久枚举点对 lca 没有做出来。

我们发现每层的每个点是等价的,于是我们的 dp 记答案就可以只记每层的一个点的答案,加和的时候乘上每层点的个数就行。有了这个思路我们可以先设 \(dp_{i, j}\) 表示第 \(i\) 层的点 \(i\) 子树中与 \(i\) 距离为 \(j\) 的点数,显然有:\(dp_{i, j} = dp_{i + 1, j - 1}\)。我们发现这样只能统计 \((u, v)\) 点对一个为另一个的祖先的方案数,根据血的教训我们不能枚举 lca 的位置我们考虑换根 dp,设 \(f_{i, j}\) 表示第 \(i\) 层的点 \(i\) 作为新树根时子树中和 \(i\) 距离为 \(j\) 的点数,显然新树 \(i\) 的子树中的点就是所有的点。容斥有:

\[f_{i, j} = f_{i - 1, j + 1} - dp_{i, j - 2} + dp_{i, j} \]

对于除了 \(dp_{i, j - 2}\) 的点中,\(i\) 与原树上的父亲 \(i - 1\) 距离为 \(j\) 的点显然距离应为 \(j + 1\),所以这样容斥是对的。

对于 \(k\) 答案应为 \(\frac{1}{2} \times \sum_{i = 1} ^ n f_{i, k} \times cnt_i\),其中 \(cnt_i\) 表示第 \(i\) 层点的个数,递归求起来是简单的。由于上面的求法求出来的显然是有序点对的个数,由于题中要求无序点对数所以乘 \(\frac{1}{2}\)。注意滚动数组优化 \(f\) 数组。

code

Fibonacci String Subsequences

看到数据范围容易想到区间 dp。我们可以拆贡献到每个 \(s\) 串的位置。设 \(dp_{i, l, r}\) 表示 \(F(i)\) 中串 \([s_l, s_r]\) 的答案。初始状态应为每个,\(dp_{0, i, i}\)\(dp_{1, i, i}\),这个很好求。对于 \(dp_{i, l, r}\),设 \(len_i\) 表示 \(F(i)\) 的长度,对于直接转移有:

\[ dp_{i, l, r} = \left\{ \begin{aligned} dp_{i - 1, l, r} \times 2 ^ {len_{i - 2}} \quad r = n\\ dp_{i - 1, l, r} \quad r \ne n\\ \end{aligned} \right. \]

\[ dp_{i, l, r} = \left\{ \begin{aligned} dp_{i - 2, l, r} \times 2 ^ {len_{i - 1}} \quad l = 1\\ dp_{i - 2, l, r} \quad l \ne 1\\ \end{aligned} \right. \]

对于 \([s_l, s_r]\) 是通过 \(F(i - 1)\)\(F(i - 2)\) 拼起来的,有:

\[dp_{i, l, r} = \sum_k dp_{i - 1, l, k} \times dp_{i - 2, k + 1, r} \]

预处理 \(len\) 数组即可。

code

Make It One

注意到 \(300000\) 之内的数一共最多有 \(6\) 个质因子,每个数的质因子又至少有一个不同,那么答案最多为 \(6\)。于是直接 dp 即可,设 \(f_i\) 表示 \(a\) 数组中 \(i\) 的倍数的个数,\(dp_{i, j}\) 表示严格选 \(i\) 个数使得最大公约数为 \(j\) 的方案数。那么答案就应为最小的 \(i\) 使得 \(dp_{i, 1} > 0\),转移用容斥也是简单的,显然有:

\[dp_{i, j} = \binom{f_j}{i} - \sum_{k = 2}^{k \times j \le m} dp_{i, j \times k} \]

时间复杂度为 \(O(6 \times m \ln m)\),其中 \(m\) 表示值域。

code

Non-Intersecting Subpermutations

题目中要求的可以直接拆成所有情况中有多少个「最大连续子数组」。显然可以直接 dp,设 \(dp_i\) 表示长度为 \(i\) 的序列末尾有一个「最大连续子数组」的答案。首先我们朴素的认为 \(dp_i = k! \times k ^ {i - k}\)。但是这样子会计重,对于 \(k = 3\) 时的 1 2 3 1 2 3 显然会记多次。因为「最大连续子数组」最多所以我们计数是计的应为最前面的「最大连续子数组」,对于上面的例子就应在 \(1\)\(4\) 位置个计一次。所以对于 dp 我们应容斥掉不合法的,\(\sum_{j = i - k + 1}^{i - 1} dp_j \times (i - j)!\),整理得:

\[dp_i = k! \times k ^ {i - k} - \sum_{j = i - k + 1}^{i - 1} dp_j \times (i - j)! \]

做完了。

code

Sets of Complementary Sums

本蒟蒻看了题解还是云里雾里。

注意到 \(Q\) 不好构造所以转而构造 \(a\)。我们先考虑当 \(\min\{a\} = 1\) 时,设和为 \(s\),显然 \(a\) 应两两不同,假设此时有 \(f\) 中合法情况,那么在此基础上对总答案的贡献就为 \((x - (s - 1) + 1) \times f\),所以我们只需要计 \(\min\{a\} = 1\) 的方案数即可。我们把所有数字减一,那么会出现一个 \(a_i = 0\),其余部分转化为:转化为 \(n − 1\) 个不同的数的和不超过 \(x − n + 1\) 的方案数。这个子问题是好 dp 的,设 \(dp_{i, j}\)\(i\) 个数和为 \(j\) 的方案数,有不重不漏的转移:

1.给原序列所有数加一,然后在末尾加 \(1\)

2.给原序列所有数加一。

总结就是:

\[dp_{i, j} = dp_{i - 1, j - i} + dp_{i, j - i} \]

答案即为 \(\sum_{n - 1 \le s \le x - n + 1} (x − (s + n − 1) + 1) dp_{n − 1, s}\)

注意咕噜咕噜数组优化。

code

Nested Segments

卡特兰数简单题。

我们可以从一个没有给定线段的区间入手。首先对于一个长度为 \(x\) 的区间,我们可以用两个大线段覆盖住然后再分治去分别覆盖两个大线段是最优的,一共可以覆盖 \(2 \times x - 1\) 个线段,反证法可证明这个策略显然最优。设 \(f_x\) 为这样的方案数,枚举断点有 \(f_x = \sum_{y = 1}^{x - 1} f_y \times f_{x - y}\),我们注意到这是个卡特兰数,有 \(f_x = C_{x - 1}\),预处理组合数可以 \(O(1)\) 地求。对于有给定线段的区间,设 \(x\) 为一个给定的大线段区间,其子线段我们定义为由 \(x\) 直接覆盖的给定的线段。我们发现对于 \(x\) 来说每一个子线段和一个单独的点是等价的,所以我们可以把每个子线段当成单独的点求然后在乘上子线段的方案数,设 \(s(x)\)\(x\) 直接覆盖的所有的点和子线段的个数总和,\(dp_x\) 为其答案,显然根据上文有:

\[dp_x = f_{s(x)} \times \prod_{to \in son(x)} dp_{to} \]

建树递归求解即可,建树弄一个栈是简单的,记得给树根手动赋一个编号为 \(0\)\([1, n]\) 的大线段然后答案直接取 \(dp_0\) 即可。做完了。

code

Count Supersequences

被诈骗题吓哭了。

考虑正难则反。总方案数显然为 \(k ^ m\)。对于 \(b\) 匹配了 \(i\)\(a\),其方案数显然为 \(\binom{m}{i} (k - 1) ^ {m - i}\)。由于 \(m\) 很大所以注意要递推求组合数。做完了,总结一下答案为:

\[k ^ m - \sum_{i = 0} ^ {n - 1} \binom{m}{i} (k - 1) ^ {m - i} \]

code

Vika and Wiki

看了 tj 才会。

手模之后注意到亦或 \(2 ^ k\) 次后,新的 \(a_i = a_i \oplus a_{(i + 2 ^ k) \mod n}\),并且我们发现操作是单调的。由于 \(2 ^ k\) 的操作的结果是好处理的,我们可以把操作拆成 \(\sum 2 ^ k\) 次操作来求,类似倍增。我们从大到小枚举 \(k\),对于每个 \(2 ^ k\) 看操作之后是否序列全为 \(0\),不为 \(0\) 就保留操作,否则不保留,最后再进行一次操作显然就是最少的操作次数(同 lca),这样操作是 \(O(n \log n)\) 的。我们发现每次对于 \(2 ^ k\) 判断时后面的和前 \(2 ^ k\) 个数显然是等价的,所以在判断 \(2 ^ k\) 时只用看前 \(2 ^ k\) 即可,这样可以优化到 \(O(n)\)

code

Small Permutation Problem (Hard Version)

判断无解很简单这里略过。

对于一段连续的 \(-1\) 我们可以一起统计,显然其单独没有意义我们可以将其视为一个整体。假如上一个不是 \(-1\) 的位置为 \(j\),这一位 \(i\) 也不是 \(-1\),我们考虑计算 \(j\) 转移到 \(i\) 的方案数。把选点弄为二维平面(图在这里),第 \(i\) 个位置选 \(p_i\) 我们定义为 \((i, p_i)\),那么我们应该选 \(a_i - a_j\) 个点在绿色和橙色的区域。我们可以枚举绿色的区域有 \(k\) 个点,由于 \(\sum k < n\) 所以这样枚举时间复杂度是对的。对于 \(k\) 个点,可以放在 \(i - j\)\(j - a_j\) 列上且每一行每一列只能放一个点,方案数有 \(\binom{i - j}{k}\binom{j - a_j}{k}k!\)。同理,我们算出橙色部分有 \(l = a_i - a_j - k\) 个点,有 \(i - j\)\(i - a_j - k\) 个列可以放,方案数为 \(\binom{i - a_j - k}{l}\binom{i - j}{l}l!\),总结一下 \(j\)\(i\) 的段答案为:

\[\sum_{k = 0} ^ {i - j} \binom{i - j}{k}\binom{j - a_j}{k}k! \binom{i - a_j - k}{l}\binom{i - j}{l}l! \]

注意人工把 \(a_n\) 赋成 \(n\)

code

Jzzhu and Numbers

容斥 + FWT 好题。

我们设 \(dp_s\) 表示与和为 \(s\) 的方案数,不好统计就容斥成与和子集为 \(s\) 的,我们注意到这就是 FWT 的与变换后的定义,我们可以先开个桶统计一下然后沃什变换一次,每个位置就成了包含 \(s\) 的数的个数,假设为 \(c_s\),那么显然有 \(dp_s = 2^{c_s} - 1\),然后再对 \(dp\) 逆变换即可。

code

posted @ 2026-05-19 16:29  ACehomoxue  阅读(9)  评论(0)    收藏  举报