【问题描述】
商店里出售n种不同品种的花。为了装饰桌面,你打算买m支花回家。你觉得放两支一样的花很难看,因此每种品种的花最多买1支。求总共有几种不同的买花的方案?答案可能很大,输出答案mod p的值。
【输入格式】
一行3个整数n,m,p,意义如题所述。
【输出格式】
一个整数,表示买花的方案数。
【输入输出样例1】
4 2 5
1
【输入输出样例1说明】
用数字1,2,3,4来表示花的种类的话,4种花里买各不相同的2支的方案有(1,2)、(1,3)、(1,4)、(2,3)、(2,4)、(3,4),共6种方案,模5后余数是1。
【输入输出样例2】
见选手目录下的flower / flower2.in与flower / flower2.out
【数据范围】
对于30%的数据,n,m≤10
对于50%的数据,n,m≤1000
对于80%的数据,1≤m≤n≤50,000
对于100%的数据,1≤m≤n≤1,000,000,p≤1,000,000,000
分析:有点技巧的数学题。n,m很大,不能递推来求,p可能是个合数,不能求逆元,唯一的方法就是尽量把C(n,m)给缩小.因为C(n,m)可以表示为一个除法式子,将其变小的一般方法就是约分,对每个数分解质因数,在次数上加加减减就可以了,最后套上快速幂.
直接上裸的会T,需要一些优化.筛法用上线性筛,在分解质因数的时候只分解没有出现过的,分解的过程中如果要分解的数是一个质数了就要退出.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; ll n, m, p, cnt, prime[1000010], ans = 1, tot[1000010]; bool vis[1000010]; void init() { for (ll i = 2; i <= 1000000; i++) { if (!vis[i]) prime[++cnt] = i; for (int j = 1; j <= cnt; j++) { if (i * prime[j] > 1000000) break; vis[i * prime[j]] = 1; if (i % prime[j] == 0) break; } } } void work(ll p,int x) { ll t = p; if (!vis[p]) { tot[p] += x; return; } for (int i = 1;prime[i] * prime[i] <= t && i <= cnt; i++) { if (p == 1) return; if (p % prime[i] == 0) { int tott = 0; while (p % prime[i] == 0) { p /= prime[i]; tott++; } tot[prime[i]] += tott * x; } if (!vis[p]) { tot[p] += x; return; } } if (x) tot[p] += x; } ll qpow(ll a, ll b, ll mod) { ll res = 1; while (b) { if (b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1; } return res; } int main() { init(); scanf("%lld%lld%lld", &n, &m, &p); for (ll i = n; i >= m + 1; i--) work(i, 1); for (ll i = 1; i <= n - m; i++) work(i, -1); for (ll i = 1; i <= cnt; i++) if (tot[prime[i]]) ans = (ans * qpow(prime[i], tot[prime[i]], p)) % p; printf("%lld ", ans); return 0; }