P3214 [HNOI2011] 卡农 (dp +排列计数 正难则反)
题目大意:
给定两个数
假设
题目分析:
-
[
]:正难则反。我们分析题目中的条件,首先对于偶数次的情况来说还算好处理,对于重复的情况,我们是否能够方便快速的处理这种情况呢,对于笔者来说太难了,在笔者的知识范围内计数问题不是计数 就是组合数学,那么运用组合数学的知识需要去掉某个集合重复 次的情况,所以我们不考虑,那么对于 来说,我们可以设计出 为前 中子集取了 个子集的方案数,而 又太大了所以说放弃,我们来考虑用 总方案数 不合法的方案数 (欢迎各位大佬指出错误) -
[
]:首先我们考虑, 的子集有多少种,根据二项式定理可知, 的子集有 种。那么我们不考虑假相同的情况,因为这个可以再最后直接除 的阶乘来处理,在这种情况下总方案数为 。 -
[
]:我们在 的基础上来考虑出现偶数次如何处理。对于一个选取 个子集使子集中所有元素出现次数为偶数的情况,我们可以由选取 个子集出现次数不固定的方案数来确定。因为对于选取 个子集的时候,对于出现偶数次的元素在第 个子集中一定不会出现,对于奇数次的元素在第 个集合中一定会出现,那么第 个集合就固定下来了,所以我们只需求出选取 个子集的总方案数即可,为 。 -
[
]:我们定义 为选取 个子集的方案数。接下来我们来考虑非空的情况,若我们在第 个子集选取了一个空集,那么这个空集对方案数不产生影响,故我们只需去掉 即可。 -
[
]:最后我们来处理子集重复的情况。若我们选的第 个子集与前面某个子集 重复,那么 的取值有 种,而去掉子集 所能构成的合法方案数为 ,最后思考 有多少种取值,我们一共有 个子集,而前面用掉了 个,故 的选取有 种。那么我可可得递推式
代码实现:
我们只需要处理出
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int mod = 1e8 + 7; const int M = 1e6 + 7; int f[M] , A[M]; int Pow(int a, int b) { int ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans; } signed main () { int n , m; cin >> n >> m; int _2 = 1; for(int i = 1; i <= n; ++ i) _2 = _2 * 2 % mod; _2 = (_2 + mod - 1) % mod; A[0] = 1; int jc = 1; for(int i = 1; i <= m; ++ i) A[i] = A[i - 1] * ((_2 - i + 1 + mod) % mod) % mod , jc = jc * i % mod; f[f[1] = 0] = 1; for(int i = 2; i <= m; ++ i) f[i] = (A[i - 1] - f[i - 1] + mod - f[i - 2] * (i - 1) % mod * (_2 - i + 2 + mod) % mod + mod) % mod; cout << f[m] * Pow(jc , mod - 2) % mod; }
[========]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现