2021牛客暑期多校训练营4补题G

首先我们先上公式的:

                

 首先说一下这个公式是怎么推出来的呢?(从大佬哪里问出来的)

        首先我们思考一下排列组合问题,我们假设就是一共有 D 个球 一共有n种颜色每一种颜色有ai个球,问将这  个球的排列方式一共有多少种呢?

首先我们思考一下每个都确定的情况下,那么如果ai 都确定了,那么这种的排列组合是不是就是 , 然后把所有的 的和为 D 的情况加起来的总和 是不是就是 , 然后两边都除  我们就会得到这个公式了。


 上面我们证明了这个公式,那么我们下面就要讨论这个题目和公式的关系了。

我们这个题目可以转换为:

这个公式。

如果没有 ai >= k的限制的话,我们的答案就是:

         

 但是我们确实是有 ai >= k 的限制,那么我们就需要解决这个问题。


         想要解决这个问题,我们需要提出容斥原理,什么是容斥原理呢?我们通过下面这个公式了解下。

 看完这个公式是不是有所了解容斥原理了,如果不了解的话,大家可以自行百度,这里就不过多得解释了。那么接下来我们如何用容斥原理来解决这个问题呢。

我们通过上面的公式是不是可以了解到里面有很多的 Ai  我们就把 上面的 Ai 事件设置为( ai 这个数小于k )那么所有的不符合要求的总和就可以用上面的公式来表示了。


        那么我们可以 用总和这个去减去不符合要求的总和然后得到正确答案

        那么我们就需要举出上面所有的情况就可以了。对于上面容斥原理的公式,我们可以得出公式的每一部分(加减的每一部分)仅仅是确实了固定的几个ai<= k  ,但是其他的 ai并没有要求。

假设。我们选了 x 个 ai 小于k ,那么这个式子的正负就是 (-1)^ x ,

       

然后从n个 ai 里面求出来 x 个数 ,那么有 C(n,x)(排列组合,从 n 个 选出来 x 个 的情况数)。

那么我们还有二个问题没有解

1.剩下的所有(n-x)个数套入这个题目公式是多少?

2.选出来这 x 数套入题目的公式结果是多少?

1.很容易发现剩下的 ai 没有要求,这里我们设定 我们选出来的这个 x 个数的和为 y 那么剩下的数套入公式可以通过这个公式来解决。 

那么就是二问题了,我们选出来的这些数带进去是多少呢?

先上代码吧。

#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<stack>
#include<set>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<map>
#include<cmath>
#define ios std::ios::sync_with_stdio(0)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const int N = 55;
const int M = N*N;
int n,k;
ll d;
ll dp[N][M];	// 选了 x 个 ai <= k 总和 为  y 
ll c[M][M]; // 代表		C(n,i) 
ll qpow(ll x,ll y)
{
	ll ans = 1;
	while(y)
	{
		if(y&1)
		{
			ans = (ans*x)%mod;
		}
		x = x*x%mod;
		y >>= 1;
	}
	return ans;
}
void init()
{
	c[0][0] = 1;
	for(ll i = 1; i <= n*k ; i ++)
	{
		c[i][0] = 1;
		for(ll j = 1 ; j <= i ; j++)
		{
			c[i][j] = (c[i-1][j]+c[i-1][j-1])%mod;
		}
	}
	dp[0][0] = 1;
	for(ll i = 1 ; i <= n ; i++)
	{
		for(ll j = 0 ; j <= i*(k-1) ; j++)
		{
			for(ll v = 0 ; v <= j && v < k ; v++)
			{
				dp[i][j] = (dp[i][j] + dp[i-1][j-v]*c[j][v]%mod)%mod;
			}
		}
	}
}

signed main(){
	cin >> n >> k >>d;
	init();
	ll ans = qpow(n,d+n*k); // 总情况(不带着分母) 
	for(ll i = 1; i <= n ; i++)
	{
		ll sgn = 0; // 正负号 
		ll mul = 1;
		for(ll j = 0 ; j <= i*(k-1) ; j++)
		{
			if(i&1) sgn = mod-1;
			else sgn = 1;			
			ans = (ans + sgn*c[n][i]%mod * qpow(n-i,d+n*k-j) %mod *dp[i][j] %mod *mul %mod)%mod;
			//  
			mul = mul*(d+n*k-j)%mod*qpow(j+1,mod-2)%mod;// 通分需要	 大家可以看到这里分母部分 还乘了 j+1(从 1 开始的) 
		} 
	}
	for(int i = d+1 , D = d+n*k ; i <= D ; i++)
	{
		ans = ans*qpow(i,mod-2)%mod; // 乘分母 同分的分母是 (D+1)*(D+2)...(D+n*k) 
	}
	printf("%lld\n",ans);
	return 0;
}





 这里面首先就是通分了,这里我们可以从主函数开始分析,我们是如何解决这个2问题的。

我们的2问题是求。选出来的ai 的带入公式的总和。

首先我们通分后,(代码中)分母是 每一项 还多了一个 j+1 的阶乘,那么也就是每一项多了一个选出来 ai和的阶乘。

那2问题应该是求这个东西。为什么要多出来这个阶乘呢?

然后再次回到 前面排列组合问题。式子的物理含义我们已经知道了,我们上面的 dp[i][j] 求得就是这个是式子的含义了。然后除以 D! 就是这里的D!也就是这里为什么要 求 j+1 的原因了。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值