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;
}
posted @ 2021-05-07 19:12  这知识他不进我的脑子  阅读(130)  评论(0编辑  收藏  举报