hdu 6397 组合数+容斥定理

http://acm.hdu.edu.cn/showproblem.php?pid=6397
题意:给三个数n,m,k, 在0~n-1中选出m个数排成一排使得他们的和等于k,这m个数可以相同,只要排列不同即可。求一共有多少种排列方式是满足题意的。

思路

这道题需要用到隔板法

我们先引入一个问题,有x个小球,放到m个盒子里,每个盒子不能为空,问有多少种放法。这里保证每个小球都是相同的,并且x>=m。分到m个盒子里,就等价于用m-1个板去隔开这x个球。因为不能有空的盒子,所以板就必须插在两个球之间,一共有x-1个空,那么答案就是从x-1个空中挑出m-1个来插板:C_{x-1}^{m-1}。如果允许有空盒子出现,就相当于额外拿来m个球,像之前一样求不允许有空盒子的情况数,然后从每个盒子拿出一个球,即是有空盒子的情况数:C_{m+x-1}^{m-1}。 如果有空盒子出现你理解不了 你可以把球想成其自身自带价值1 或 0 然后将这些有价值的球放在一起进行隔板法 为什么加m个? 因为加m个 到时候采用隔板法的时候 你最后有m-1个空格能够插隔板 那么就是极值的情况就是 k+0 0 0 (这里用三个盒子来举例) 

这道题的k,就相当于有k个小球,放到m个盒子里,如果不考虑Xi的约束条件,答案就是。我们考虑,这些答案里会有某些,我们应该把这些情况去掉。当有一个的时候,有种Xi。此时就相当于k个小球中,已经有n个小球放到了某个盒子里,接下来把剩下的k-n个小球放到m个盒子里就是有这么多种情况。所以我们用,这样的话,多减去了有两个的情况,我们又要加回来。假如有一个Xi>=n的时候,有C_{m}^{1}种Xi。此时就相当于k个小球中,已经有n个小球放到了某个盒子里,接下来把剩下的k-n个小球放到m个盒子里就是有这么多种情况C_{m+k-n-1}^{m-1}。这样的话,多减去了有两个Xi>=n的情况,我们又要加回来。考虑到这里,就不难发现,这可以用容斥原理解决。有2个盒子大于n 的情况里肯定包含了 有1个盒子大于n的情况 有3个盒子大于n 的情况里肯定包含了 有2个盒子大于n的情况   这样进行容斥。

最后得出:

        ans=C_{m+k-1}^{m-1}+\sum_{i=1}^{m}C_{m}^{i}*C_{m+k-i*n-1}^{m-1}*(-1)^{i}
 

#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const ll N=5000000+5;
const ll mod=998244353;
int F[N],Finv[N],inv[N];//F 是阶乘 Finv是逆元的阶乘 
void init ()
{
	inv[1]=1;
	for(int i=2;i<N;i++)
	{
		inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
	}
	F[0]=Finv[0]=1;
	for(int i=1;i<N;i++)
	{
		F[i]=F[i-1]*1ll*i%mod;
		Finv[i]=Finv[i-1]*1ll*inv[i]%mod;
	}
}
ll comb(int n,int m) //这里需要开ll 不然会wa 
{
	if(m<0||m>n||n<0) // 不n,m清楚谁在下面 谁在上面 可以从这里判断 
	{
		return 0;
	}
	return F[n]*1ll*Finv[n-m]%mod*Finv[m]%mod;
}
int main()
{
	init();
	int t;
	cin>>t;
	while(t--)
	{
		int n,m,k;
		cin>>n>>m>>k;
		ll ans=comb(m+k-1,m-1); 
		for(int i=1;i*n<=k;i++)
		{
			if(i&1)
			{
				ans=(ans-(comb(m,i)*comb(m+k-i*n-1,m-1)%mod)+mod)%mod;
			}
			else
			{
				ans=(ans+(comb(m,i)*comb(m+k-i*n-1,m-1)%mod))%mod;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

 

展开阅读全文

没有更多推荐了,返回首页