小a的学期 (组合数取模模板)
题目描述
小a是一个健忘的人,由于他经常忘记做作业,因此老师对他很恼火。
小a马上就要开学了,他学期一共2n
小a马上就要开学了,他学期一共2n
天,对于第i天,他有可能写了作业,也可能没写作业,不过他自己心里还有点B数,因此他会写恰好n天的作业
现在,小a需要安排他的学期计划,如果小a的学期中存在一天x,在这之前的x天中,他没写作业的天数 - 写作业的天数⩾k
,那么老师就会把它开除,我们称这是一种不合法的方案
小a想知道他有多少种合法的方案
小a想知道他有多少种合法的方案
输入描述:
第一行三个整数n,k,p,p表示对p取模
输出描述:
一个整数表示答案
示例1
输入
2 1 100007
输出
2
说明
总共有2n=4
天
合法的方案有
写了 没写 写了 没写
写了 写了 没写 没写
注意:没写 写了 没写 写了 是一种不合法的方案,因为在第一天时没写的天数-写了的天数⩾1
示例2
输入
10 5 10000007
输出
169252
思路:在x位置之前出现m+k个不读书,m个读书则为不合法序列,将后面的不读书和读书互换,则该序列中含有n+k个不读书和n-k个读书
合法方案$C^{n}_{2n}$ 不合法方案$C^{n+k}_{2n}$ 答案相减即可
因为p不一定为质数,所以不能直接做。
$C^{m}_{n}=\dfrac {n!}{m!\left( n-m\right) !}$ 把上下所有质因子的指数求出来,在快速幂算即可。
#include <bits/stdc++.h> #define ll long long using namespace std; template<typename T> inline void read(T &x) { x = 0;T f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } x *= f; } const int N = 2e6; ll MOD, n, m; bool vis[N + 10]; int prime[N], prin; void getpri() { for (int i = 2; i <= N; i++) { if (!vis[i]) prime[++prin] = i; for (int j = 1; j <= prin && i * prime[j] <= N; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } ll qp(ll a, ll b) { ll res = 1; while (b) { if (b & 1) res = res * a % MOD; a = a * a % MOD; b >>= 1; } return res; } // 计算x!中素因子p的指数 ll cal(ll x, ll p) { ll res = 0; ll temp = p; while (x >= temp) { res += x / temp; temp *= p; } return res; } ll solve(ll n, ll m) { ll ans = 1; for (int i = 1; i <= prin && prime[i] <= n; i++) { ll cnt = cal(n, prime[i]) - cal(m, prime[i]) - cal(n - m, prime[i]); ans = (ans * qp(prime[i], cnt)) % MOD; } return ans; } int main() { getpri(); read(n); read(m); read(MOD); printf("%lld\n", (solve(n * 2, n) - solve(n * 2, n + m) + MOD) % MOD); return 0; }

浙公网安备 33010602011771号