stone (组合数学 + Lucas定理)


 
传送门
 
解题思路:第i组的人数必须大于Ci,于是我们可以将问题转化为\(N-\sum_{i=1}^M Ci\)人分到M组中,且保证每一组的人数大于0,然后我们可以使用隔板法求出分的的组数\(C_{N-1-\sum_{i=1}^M Ci}^{m-1}\)
我们可以直接通过基本的组合公式+费马小定理直接求,也可以通过Lucas定理求得:
直接求:
Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 1000000007
ll n,m,a,t;

ll power(ll a,ll b) {
	ll ans = 1;
	while(b) {
		if(b&1) ans = (ans*a)%mod;
		a = (a*a)%mod;
		b>>=1;
	}
	return ans;
}

ll inv(ll x) {
	return power(x,mod-2);
}

ll C(ll a,ll b) {
	ll ans =  1;
	for(ll i = 1;i <= b; ++i) {
		ans = (ans*(a-i+1)) % mod;
		ans = (ans*inv(i)) % mod;
	}
	return ans % mod;
}

int main()
{
	while(~scanf("%lld%lld",&n,&m)) {
		a = 0LL;
		for(int i = 0;i < m; ++i) {
			scanf("%lld",&t);
			a += t;
		}
		printf("%lld\n",C(n-a-1,m-1));
	}

	return 0;
}

 
Lucas定理:

#include <iostream>
#include <cstdio>
using namespace std;

#define MOD 1000000007
typedef long long LL;

int Read() { //快读
	char c; int ans(0);
	while(!isdigit(c = getchar()));
	do ans = ans * 10 + c - 48;
	while(isdigit(c = getchar()));
	return ans;
}

int n, m;

LL Qpow(LL a, LL b, LL p) { //快速幂 
	LL ans = 1;
	for(; b; b>>=1, (a*=a) %= p)
		if(b & 1) (ans *= a) %= p;
	return ans;
}

LL c(LL n, LL m, LL p) {
	if(n < m) return 0;
	if(m > n - m) m = n - m;
	LL s1 = 1, s2 = 1;
	for(int i=0; i<m; i++) {
		s1 = s1 * (n - i) % p;
		s2 = s2 * (i + 1) % p;
	}
	return s1 * Qpow(s2, p-2, p) % p;
}

LL Lucas(LL n, LL m, LL p) { //Lucas
	if(m == 0) return 1;
	return c(n % p, m % p, p) * Lucas(n / p, m / p, p) % p;
}

int main() {
	n = Read(), m = Read();
	for(int i=1; i<=m; i++) n -= Read();
	printf("%d\n", Lucas(n-1, m-1, MOD));
	return 0;
}
posted @ 2021-01-26 13:58  MangataTS  阅读(104)  评论(0编辑  收藏  举报