[BeiJingWc2008]Gate Of Babylon

本文介绍了一道关于组合计数的问题,即计算金皮卡从无限及有限数量的宝具中选取不超过M件进行搭配的方法总数,并通过模P求余数的方式给出答案。文章详细展示了使用容斥原理和组合数学技巧解决问题的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

一天金皮卡突发奇想:如果从自己无尽的财宝里面,随便抽不超过M件宝具出来砸死敌人的话。一共有多少种搭配方法呢-_-?
假设金皮卡一共有N种不同类型的宝具,大部分类型的宝具都有无限多,但其中T种超级神器的数量是有限的。设第i种超级神器的数量不超过Bi件。
若相同类型的宝具数量相同,则认为是相同的搭配方案。
金皮卡知道方案数会很大,从小数学成绩就好的他挑选了一个质数P,请你帮他计算一下方案数模P后的余数。
注意,一件也不选也是一种方案。

思路

T个东西容斥原理。

当场笔记如下:

C(n,m) % p 怎么算?
n!/(m!*(n-m)!) %p
n! 有很多个p, 分母也有很多个p, 且上边个数>=下边,(若大于,则%p=0)
上边个数=下边
先算(n!跑掉所有的p因子)%p
n!%p=n*(n-1)*...2*1 % p
=1*2*...(p-1)*1*1*2*...*(p-1)*2*1*2*...(p-1)...             (1*2*3) the REST
预处理n!%p (n=1-p-1)
n!%p=(p-1)!^(n/p) * REST * (n/=p的递归值)

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
int N,P,T,M;
int B[20];
int fctr[100001];
int revs[100001];
int gcd(int a,int b,int &x,int &y)
{
	if (a==0) {x=0;y=1;return b;}
	int tx,ty,ret;
	ret=gcd(b%a,a,tx,ty);
	x=ty-(b/a)*tx;y=tx;
	return ret;
}
int cp(int n)
{
	int ret=0;
	while(n)
	{
		ret+=n/P; n/=P;
	}
	return ret;
}
int pow(int a,int b)
{
	int ret=1;
	while (b)
	{
		if (b&1) ret=(long long )ret*a%P;
		a=(long long )a*a%P;
		b>>=1;
	}
	return ret;
}
int calc(int n)
{
	int ret=1,tmp=0;
	while(n)
	{
		ret=(long long)ret*fctr[n%P]%P;
		tmp+=n/P;
		n/=P;
	}
	ret=(long long)ret*pow(fctr[P-1],tmp)%P;
	return ret;
}
int Comb(int n,int m)
{
	if (n<m) return 0;
	if (cp(n) != cp(m)+cp(n-m))
		return 0;
	int t1=calc(n),t2=(long long)calc(m)*calc(n-m)%P;
	return (long long)t1*revs[t2]%P;
}
int solve()
{
	scanf("%d%d%d%d",&N,&T,&M,&P);
	fctr[0]=1;
	for (int i=1;i<P;i++)
		fctr[i]=(long long)fctr[i-1]*i%P;
	int tx,ty;
	for (int i=1;i<P;i++)
	{
		gcd(i,P,tx,ty);
		revs[i]=(tx%P+P)%P;
	}
	for (int i=0;i<T;i++)
	{
		scanf("%d",B+i);
		B[i]++;
	}
	int tn,tm;
	int ret=0,cc;
	for (int i=0;i<(1<<T);i++)
	{
		cc=0;
		tm=N+M;
		for (int j=0;j<T;j++)
			if (i &(1<<j))
				tm-=B[j],cc++;
		if (cc%2==0) ret+=Comb(tm,N);
		else ret-=Comb(tm,N);
		ret=(ret%P+P)%P;
	}
	printf("%d\n",ret);
}
int main()
{
	solve();
	return 0;
}

链接 http://www.lydsy.com/JudgeOnline/problem.php?id=1272

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值