Atcoder abc110D.Factorization【隔板法】
题目链接
思路
隔板法:将\(n\)个球放入\(m\)个盒子中,盒子可以为空,那么方案数为\(C_{n+m-1}^{m-1}\)
证明:用隔板法相当于在n个球中间插入\(m-1\)块板子,但是有可能会出现连续空着的盒子出现,例如“xx|xx|||xx”的情况(用x代表球,|代表插入的隔板)。那么最好的办法就是增加\(m\)个球,假装每个盒子内至少有一个球。假设某个盒子内球的数量为\(x\)个,那么盒子内实际的球数为\(x-1\)个。相当于将\(n+m\)个球分割为\(m\)个空间,每个空间内至少要有一个球。所以方案数即为\(C_{n+m-1}^{m-1}\).
对于本题思路即是对于\(m\)进行质因子分解,\(m=p_1^{a_1}*p_2^{a_2}*p_3^{a_3}*...\),每一个质因子都是相对独立的,那么对于一个质因子就变成独立的小问题:将\(a_i\)个球放入\(n\)个盒子的方案数,总方案数就是将每一个质因子的方案数乘起来即可。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
#define gcd __gcd
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
LL f[N], inv[N];
vector<int> nums;
bool st[N];
LL kpow(LL a, LL n) {
LL res = 1;
while(n) {
if(n & 1) res = res * a % mod;
n >>= 1;
a = a * a % mod;
}
return res;
}
void init() {
int n = 2e5;
f[0] = f[1] = 1;
inv[0] = inv[1] = kpow(1, mod - 2);
for(int i = 2; i <= n; i++) {
f[i] = 1LL * f[i - 1] * i % mod;
inv[i] = 1LL * inv[i - 1] * kpow(i, mod - 2) % mod;
if(!st[i]) nums.push_back(i);
for(int j = 0; j < nums.size() && i <= n / nums[j]; j++) {
st[i * nums[j]] = true;
if(i % nums[j] == 0) break;
}
}
}
LL C(LL a, LL b) {
return f[a] * inv[b] % mod * inv[a - b] % mod;
}
void solve() {
int n, m;
scanf("%d%d", &n, &m);
LL res = 1;
for(int i = 0; i < nums.size() && nums[i] <= m / nums[i]; i++) {
int cnt = 0;
while(m % nums[i] == 0) {
cnt++;
m /= nums[i];
}
if(cnt > 0) {
res = 1LL * res * C(n + cnt - 1, n - 1) % mod;
}
}
if(m > 1) {
res = 1LL * res * C(n, n - 1) % mod;
}
printf("%lld\n", res);
}
int main() {
init();
// freopen("in.txt", "r", stdin);
// int t; cin >> t; while(t--)
solve();
return 0;
}