P2606 [ZJOI2010]排列计数(计数dp + Lucas定理)
P2606 [ZJOI2010]排列计数(计数dp + Lucas定理)
题目传送门
题目大意:
对于
题目分析:
-
[
]:首先我们知道了要找到小根堆的方案数,那么在根节点的数值一定是最小的。 -
[
]:我们先来考虑 ,那么如何设计状态呢,我们发现对于确定的长度 ,我们可以确定他的形状,那么我们便可以确定他的方案数,所以我们令 为长度 的排列符合小根堆的情况。那么如何转移呢,对于 ,我们是可以确定根节点的,那么只有 个点是不确定的,而我们还可以确定左子树和右子树的大小,而对于左子树我们还有 种选法,对于每种选法有 种符合的,随之也确定下来了右子树,那么可以得出
代码实现:
- [
]:因为是给定的模数,难免会出现 时 的情况,所以我们需要用到 定理来解决。
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; int n , m; const int M = 1e6 + 7; int Pow(int a , int b) { int ans = 1; while(b) { if(b & 1) ans = ans * a % m; a = a * a % m; b >>= 1; } return ans; } int fac[M] , f[M] , Log[M] , inv[M]; int C(int x, int y) {//Lucas 定理 if (!y) return 1; int u = C(x / m, y / m), v = x % m, w = y % m, z; if (v < w) z = 0; else z = 1ll * (1ll * fac[v] * inv[w] % m) * inv[v - w] % m; return 1ll * u * z % m; } signed main () { ios::sync_with_stdio(0),cin.tie(0); cin >> n >> m; fac[0] = 1; Log[0] = -1;//处理子树需要用到 for(int i = 1; i <= n; ++ i) fac[i] = fac[i - 1] * i % m, Log[i] = Log[i >> 1] + 1;; for(int i = 0; i <= n; ++ i) inv[i] = Pow(fac[i] , m - 2); f[1] = 1 , f[2] = 1 , f[3] = 2; int l = 1 , r = 1; for(int i = 4; i <= n; ++ i) { if (i - (1 << Log[i]) + 1 <= (1 << Log[i] - 1)) l ++; else r ++; f[i] = C(i - 1 , l) * f[l] % m * f[r] % m; } cout << f[n]; }
[========]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现