「SOL」打扫笛卡尔cartesian (模拟赛)
为什么会有人推得出来第三题想不出来签到题啊 (⊙_⊙)?
题面
有一棵有根树
记
给定
答案对给定的正整数
解析
先分析在
- 只有一个儿子时,有
的概率会走向该儿子; - 有两个儿子时,有
的概率第一次就走向该儿子,有 的概率第二次走向该儿子,即总共有 的概率会走向该儿子。
于是我们发现是否会到达一个儿子的概率恒为
考虑到笛卡尔树本身是一个分治结构——从最小值处划分为两个区间分别建笛卡尔树,而一个排列建立笛卡尔树仅仅与排列的元素个数有关。由此可以设计一个以排列元素大小为状态的 DP。
设
有一个非常常用的性质:
转移则考虑枚举左子树的大小
先不管
这么多阶乘容易让人联想到指数生成函数的样子,不妨化一下:
显然可以把
有一个类似于调和级数前
现在回头看一看
同样的,我们记
对
我们要算的答案是
这样分母就可以全部抵消了,预处理调和级数前
源代码
Copy/* Lucky_Glass */
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N = 1e7 + 10;
typedef long long llong;
int mod;
inline int reduce(llong key) {
return int((key %= mod) < 0 ? key + mod : key);
}
int pre[N], suf[N];
int main() {
freopen("cartesian.in", "r", stdin);
freopen("cartesian.out", "w", stdout);
int n; scanf("%d%d", &n, &mod);
pre[0] = 1;
for (int i = 1; i <= n; ++i) pre[i] = reduce(1ll * pre[i - 1] * i);
suf[n + 1] = 1;
for (int i = n; i; --i) suf[i] = reduce(1ll * suf[i + 1] * i);
int ans = 0;
for (int i = 1; i <= n; ++i)
ans = reduce(ans + 1ll * pre[i - 1] * suf[i + 1]);
int ex_ans = 0;
for (int i = 2, tmp = 0; i <= n; ++i) {
tmp = reduce(pre[i - 2] + (i - 1ll) * tmp);
ex_ans = reduce(ex_ans + 1ll * tmp * suf[i + 1]);
}
ans = reduce(1ll * ans + ex_ans);
printf("%d\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现