华中科大邀请赛 Balls(组合数学+Lucas 定理+思维)

题目链接:

Balls

 

题意:

给定 k,S(1<=k,S<=1e18),p(2<=p<=5e6),求  \sum\prod_{i=1}^{k}xi[xi>=1][\sum_{i=1}^{k}xi<=S]  。

 

思路:

需要把这个公式转换成组合数学问题:

相当于有 S 个小球,分成 k 堆,可以有剩余小球,然后把每堆小球个数乘起来得到这种情况的价值,最后求的就是所有情况的价值之和。

每堆小球个数乘起来可以理解为:从每堆小球中任取一个,有多少种取法。

然后就考虑如何分组,使用隔板法。要分成 k 组,且可以有剩余,那么相当于加 k 个隔板。默认最前面有一个隔板(实际不存在),两个隔板之间为一个分组。(如图,S=8,k=3的情况)

我们把隔板也看作一个个小球,那么总共就有 k+S 个小球。从中任取 k 个作为隔板。这样会出现不合法的情况,即第一个为隔板或两个隔板是相邻的,这样就会有一些分组中小球个数为 0 个。

非常神奇的做法来了:

我们在这 k+S 个小球中任选 2k 个,其中第奇数个作为从一组中选出的小球,第偶数个作为隔板。这情况数就是答案!且巧妙的避开了上述的不合法情况!

最后,ans = C(s+k,2k) % p,因为 s,k 很大,但 p 较小,因此可以用 Lucas 定理进行计算:

C(n,m)%p = C(n/p,m/p)*C(n%p,m%p)%p

预处理C(p,p)以内的组合数,然后递归计算即可。

 

Code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 5e6+ 10;

ll k, s, p;

ll F[MAX], Finv[MAX], inv[MAX];//F是阶乘,Finv是逆元的阶乘 

void init(ll MOD) {
	inv[1] = 1;
	for (int i = 2; i < MAX; i++) {
		inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD;
	}
	F[0] = Finv[0] = 1;
	for (int i = 1; i < MAX; i++) {
		F[i] = F[i - 1] * 1ll * i % MOD;
		Finv[i] = Finv[i - 1] * 1ll * inv[i] % MOD;
	}
}

ll comb(ll n, ll m, ll MOD) {//comb(n, m)就是C(n, m) 
	if (m < 0 || m > n) return 0;
	return F[n] * 1ll * Finv[n - m] % MOD * Finv[m] % MOD;
}

ll cul(ll n, ll m, ll MOD)
{
	if (m <= MOD && n <= MOD) {
		return comb(n, m, MOD);
	}
	return cul(n / MOD, m / MOD, MOD)*comb(n%MOD, m%MOD, MOD) % MOD;
}

int main()
{
	scanf("%lld%lld%lld", &k, &s, &p);
	if (s < k) {
		printf("0\n");
		return 0;
	}
	init(p);
	ll ans = cul(s + k, 2 * k, p);
	printf("%lld\n", ans);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值