专栏文章
题解:CF1439D INOI Final Contests
CF1439D题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @mip4r5i1
- 此快照首次捕获于
- 2025/12/03 06:08 3 个月前
- 此快照最后确认于
- 2025/12/03 06:08 3 个月前
锣鼓题解一篇也没看懂,我太🥬了!不过有一说一,所有的题解都感觉是知道了最终结果然后反过来写的“代码解说”一类的东西,没有思路比较顺畅的😨。
-
其实这种贡献和看到之后的第一反应应该是转期望,然后就可以只用维护一个 dp 值了。但是实际上没办法转期望来做,这将在之后被说明。
-
我们先考虑一个 dp 对象:记 表示一共有 个位置, 个人的时候,这 个人都找到了位置坐的情况下,所有的 的权值之和。转移考虑枚举第 个人坐的位置 ,不妨设 ,那么 可以取 ,问题来了,我们没办法确定 是否有人坐了,所以我们不能知道是否每一个 中的数都可以作为 。而这个信息也不能塞到状态里面,于是这个转移思路就倒闭了。
-
这个做法倒闭的原因是你没法确定 的被占用情况。这时候有一种特殊情形:如果 ,那么假如最终 坐到了 ,那么 一定都是已经被占用了的,所以 可以取 中任意的一个数。那么我们只需要把前 个数分到 两块里面。假如我们得出了 的结果,那么可以把 的情形按照位置占用情况分成若干个极长被占用位置连续段,然后就可以转化为 的若干段拼起来的结果了。
-
容易发现这个贡献的计算需要知道方案数,所以我们先求一个 表示有 个位置, 个人,然后使得每个人都找到了位置坐的 对数。转移枚举第 个人所在位置 :
- 先考虑第 个人的贡献:如果 ,那么 都可以;如果 ,那么 都可以。所以 共 种。
- 然后我们考虑前 个人的贡献,实际上就是把它们分到两块里面,这里分组的方案数是 ,然后这两段的贡献分别是 。
即 。 -
然后记 表示有 个位置, 个人,每个人都找到了位置,此时所有方案的 之和。转移思路就跟上面的方案数类似了,只是在算贡献的时候原本对于方案数,贡献是 ,而对于权值和,贡献就是权值了(我在说什么废话?)。
- 第 个人的贡献:。
- 前 个人的贡献:。
你发现这里面贡献计算方式是乘积,所以你转期望就相当于转了个寂寞。而且这题转了期望之后并不能将答案写成若干个概率之和的形式,所以倒闭了。 -
接下来我们来合并求出 的情况,记 表示有 个位置, 个人,然后使得每个人都找到了位置坐的 对数。考虑一点简单递推,设 为 中最长的全部被占用了的前缀长度,枚举 。
-
如果 ,就是 。
-
否则 ,考虑扔掉 这一段,那么 位置肯定得是 ,所以就剩下一段长为 的子结构。所以贡献为 。
-
-
记 表示有 个位置, 个人,然后使得每个人都找到了位置坐的贡献总和。设 为 中最长的全部被占用了的前缀长度,枚举 。
-
如果 ,就是 。
-
否则 ,贡献为 。
-
-
最终答案就是 ,初始值就是 ,其他的项都是 。需要预处理组合数。
-
的计算是 的, 的计算是 的。出题人说有 做法,我不会,代码看不懂。感觉是推式子神秘做法,我太🥬了。
给出参考代码。
CPP#include <bits/stdc++.h>
using namespace std;
using tp = int;
constexpr tp INF = 1e9;
#define FULL(a) begin(a), end(a)
#define ALL(a, l, r) begin(a) + l, begin(a) + r + 1
template <typename T>
bool ckmax(T &x, T y) {
if (y > x) return x = y, 1;
return 0;
}
template <typename T>
bool ckmin(T &x, T y) {
if (y < x) return x = y, 1;
return 0;
}
tp mod;
tp norm(tp x) { return x + (x < 0) * mod - (x >= mod) * mod; }
void add(tp &x, tp y) { x = norm(x + y); }
tp pow(tp x, tp y) {
tp z = 1;
for (; y; x = 1ll * x * x % mod, y >>= 1)
if (y & 1) z = 1ll * z * x % mod;
return z;
}
void QWQ() {
ios::sync_with_stdio(false), cin.tie(nullptr);
}
constexpr tp N = 505;
tp n, m;
array<tp, N> fac, ifc, f1, f2;
array<array<tp, N>, N> c, g1, g2;
tp bnm(tp n, tp m) {
if (n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * ifc[m] % mod * ifc[n - m] % mod;
}
tp HARUTO() {
cin >> n >> m >> mod;
for (tp i = 0; i <= n; ++i) {
c[i][0] = 1;
for (tp j = 1; j <= i; ++j) c[i][j] = norm(c[i - 1][j - 1] + c[i - 1][j]);
}
f1[0] = 1;
for (tp i = 1; i <= n; ++i) {
for (tp j = 1; j <= i; ++j) {
add(f1[i], 1ll * f1[j - 1] * f1[i - j] % mod * c[i - 1][j - 1] % mod);
tp w = 1ll * f1[j - 1] * f2[i - j] % mod + 1ll * f2[j - 1] * f1[i - j] % mod;
add(f2[i], 1ll * (i + 1) * norm(w) % mod * c[i - 1][j - 1] % mod);
add(f2[i], 1ll * norm(c[j][2] + c[i - j + 1][2]) * f1[j - 1] % mod * f1[i - j] % mod * c[i - 1][j - 1] % mod);
}
f1[i] = 1ll * (i + 1) * f1[i] % mod;
}
g1[0][0] = 1;
for (tp i = 1; i <= n; ++i) {
for (tp j = 0; j < i; ++j) {
g1[i][j] = g1[i - 1][j], g2[i][j] = g2[i - 1][j];
for (tp l = 1; l <= j; ++l) {
add(g1[i][j], 1ll * f1[l] * g1[i - l - 1][j - l] % mod * c[j][l] % mod);
add(g2[i][j], 1ll * f1[l] * g2[i - l - 1][j - l] % mod * c[j][l] % mod);
add(g2[i][j], 1ll * f2[l] * g1[i - l - 1][j - l] % mod * c[j][l] % mod);
}
}
g1[i][i] = f1[i], g2[i][i] = f2[i];
}
cout << g2[n][m] << "\n";
return 0;
}
int main() {
QWQ();
tp t = 1;
while (t--) HARUTO();
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...