动态线条
动态线条end

国家集训队]礼物

[国家集训队]礼物 

前置知识:

扩展卢卡斯定理

正文:

emmm,我可以说这是一道模板题吗。。。。

首先我们来分析题意:

简单来说,就是一共有 \(n\) 个不同的礼物和 \(m\) 个人,第 \(i\) 个人需要 \(w_i\) 个礼物,问一共有多少种不同的方案数。

我们可以很轻松地看出这是一道与组合数有关的题目,对于总方案数,我们可以将其分解为 \(m\) 个不同的方案数的乘积(乘法原理),我们以题中的样例一为例:现在一共有 \(4\) 个礼物和 \(2\) 个人,第一个人要 \(1\) 个礼物,则第一个人取走礼物的方案数为 \(C_4^1\) 种;第二个人要 \(2\) 个礼物,而在第一个人取走礼物后,还剩下 \(3\) 个礼物,所以第二个人取走礼物的方案数为 \(C_3^2\) 种。所以,总方案数就为 \(C_4^1 * C_3^2=12\) 种。

现在我们改变取走礼物的顺序:让第二个人先取走两个礼物,这时第二个人取走礼物的方案数为 \(C_4^2\)种,再让第一个人取走礼物的方案数,此时还剩下 \(2\) 个礼物,也就是说,第一个人取走礼物的方案数有 \(C_2^1\) 种。所以,总方案数就为 \(C_4^2*C_2^1=12\) 种。

我们可以发现,无论谁先取走礼物,最终结果都是一样的,所以我们只需按所给的礼物数量顺序计算就行。也就是说,总方案数的式子为:

\[\begin{align} ans=C_n^{w_1}+C_{n-w_1}^{w_2}+\cdots +C_{n-w_1-w_2\cdots-w_m}^{w_m} \end{align} \]

然鹅,题目中还有无解的情况,那什么情况下会无解呢?很显然,只有在礼物不够用时才会无解,所以我们可以在计算答案的同时判断剩余的礼物是否为负数,若是,则输出 \(Impossible\) ,否则继续计算。

到这里为止,我们已经知道如何计算最终答案了,只要在途中取模就能得到答案,是不是很简单?

不,你想的太简单了

我们来看一下这一题的

emm,这模数有点大,感觉总有些不对的地方,再仔细看看。

???,什么情况?\(P\) 不一定为质数???

既然P不一定为质数,那我们就没法求组合数了啊,怎么办呢?这时候,便要用到扩展卢卡斯定理了。

什么?你问我什么是扩展卢卡斯定理?你康康这里别问我为什么这叫扩展卢卡斯,我也不知道他到底和卢卡斯定理有啥关系

总而言之,只要用了扩展卢卡斯定理,我们就能解决模数 \(P\) 不是质数的情况,这也是这道题真正的考点。接下来,只要带入模板,将模数分解计算即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll pow_mod(ll x,ll n,ll mod){
	ll res=1;
	while(n>0){
		if (n&1) res=res*x%mod;
		x=x*x%mod;
		n>>=1;
	}
	return res;
}

ll exgcd(ll a,ll b,ll &x,ll &y){
	if (b==0){
		x=1;
		y=0;
		return a;
	}
	ll d=exgcd(b,a%b,x,y);
	ll t=x;
	x=y;
	y=t-a/b*y;
	return d;
}

ll CRT(ll n,ll *a,ll *b){
	ll M=1,res=0;
	for (ll i=0;i<n;i++) M=M*b[i];
	for (ll i=0;i<n;i++) {
		ll x,y;
		ll g=M/b[i];
		exgcd(g,b[i],x,y);
		res=(res+g*x*a[i])%M;
		if (res<0) res+=M;
	}
	return res;
}

ll cal(ll n,ll p,ll mod){
	if (n==0) return 1;
	ll res=1;
	for (ll i=1;i<=mod;i++){
		if (i%p!=0){
			res=res*i%mod;
		}
	}
	res=pow_mod(res,n/mod,mod);
	for (ll i=1;i<=n%mod;i++){
		if (i%p){
			res=res*i%mod;
		}
	}
	return res*cal(n/p,p,mod)%mod;
}

ll work(ll n,ll m,ll p,ll mod){
	ll cnt=0;
	for (ll i=n;i;i/=p) cnt+=i/p;
	for (ll i=m;i;i/=p) cnt-=i/p;
	for (ll i=n-m;i;i/=p) cnt-=i/p;
	return pow_mod(p,cnt,mod)*cal(n,p,mod)%mod*
		   pow_mod(cal(m,p,mod),mod/p*(p-1)-1,mod)%mod*
		   pow_mod(cal(n-m,p,mod),mod/p*(p-1)-1,mod)%mod;
}
ll com(ll n,ll m,ll p){
	if (n<m) return 0;
	ll a[20],b[20],tot=0;
	for (int i=2;i*i<=p;i++){
		if (p%i==0){
			b[tot]=1;
			while(p%i==0) p/=i,b[tot]*=i;
			a[tot]=work(n,m,i,b[tot]);
			tot++;
		}
	}
	if (p>1){
		b[tot]=p;
		a[tot]=work(n,m,p,b[tot]);
		tot++;
	}
	return CRT(tot,a,b);
}
int main(){
	ll p;
	scanf("%lld",&p);
	ll n,m;
	scanf("%lld %lld",&n,&m);
	ll ans=1;
	for (int i=0;i<m;i++){
		ll w;
		scanf("%lld",&w);
		if (n<0 || n<w){
			puts("Impossible");
			return 0;
		}
		ans=ans*com(n,w,p)%p;
		n-=w;
	}
	printf("%lld\n",ans);
	return 0;
}

最后:ac第一道黑题祭怎么感觉这道题有点水呢)

都看到这里了,点个赞吧各位大佬萌。

posted @ 2020-06-12 16:18  冘木  阅读(238)  评论(0编辑  收藏  举报
Live2D
动态线条
动态线条end