1. 程式人生 > >Character Encoding(組合數學相關)

Character Encoding(組合數學相關)

題目大意:

    給你 n,m,k,讓你求  x_0+x_1+...+x_m = k(0\leqslant x_i< n)  方程的解的個數。

題解:

    如果 x_i 的限制只是 0\leqslant x_i 則通過k個球,m個箱子的插板法很容易得出解的個數為 C_{m+k-1}^{m-1}

    之後在那麼多的解的個數中,還要排除所有 x_i\geqslant n 的解

    先排除有一個 x_i\geqslant n 的情況,其個數為 C_{m}^{1}*C_{m+k-1-n}^{m-1} 

    排除之後,有發現多排除了有兩個 x_i\geqslant n 的情況,於是又要把它加回來,個數為C_{m}^{2}*C_{m+k-1-2*n}^{m-1}

    而在加回來之後,有發現多加了有三個 x_i\geqslant n 的情況,於是又要把他們減掉

    其中,例如,C_{m}^{2} 表示有 2列n個球在m個箱子中,而C_{m+k-1-2*n}^{m-1}表示減去2*n個球之後,剩下的球在m個箱子中放的方案數

    總結就是運用組合數的容斥原理,最終結果為

 AC程式碼:

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MOD = 998244353;
const ll N = 300010;
ll jc[N];
void init()
{
	ll i;
	jc[0] = 1;
	for(i=1;i<N;i++)
		jc[i] = jc[i-1]*i%MOD;
}
ll m_pow(ll n,ll p)
{
	if(p == 0)
		return 1;
	if(p&1)
		return m_pow(n*n%MOD,p>>1)*n%MOD;
	else
		return m_pow(n*n%MOD,p>>1)%MOD;
}
ll inv(ll n)
{
	return m_pow(n,MOD-2);
}
ll C(ll a,ll b)
{
	if(a < b)
		return 0;
	return jc[a]*inv(jc[b]*jc[a-b]%MOD)%MOD;
}
int main()
{
	init();
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,m,k;
		ll ans=0;
		ll i;
		scanf("%d%d%d",&n,&m,&k);
		if(k == 0)
		{
			printf("1\n");
			continue;
		}
		if(k < n)
		{
			ans = C(m+k-1,m-1);
			printf("%lld\n",ans);
			continue;
		}
		if(k > m*(n-1))
		{
			printf("0\n");
			continue;
		}
		ans = C(m+k-1,m-1);
		for(i=1;i<=m;i++)
		{
			if(i&1)
				ans -= C(m,i)*C(m+k-1-i*n,m-1)%MOD;
			else
				ans += C(m,i)*C(m+k-1-i*n,m-1)%MOD;
			ans = (ans+MOD)%MOD;
		}
		printf("%lld\n",ans);
	}
	return 0;
}