2142: 礼物
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1820 Solved: 764
[ Submit][ Status][ Discuss]
Description
一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E
心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。小E从商店中购买了n件礼物,打算送给m个人
,其中送给第i个人礼物数量为wi。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某
个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模P后的结果。
Input
输入的第一行包含一个正整数P,表示模;
第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;
以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。
Output
若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。
Sample Input
100
4 2
1
2
4 2
1
2
Sample Output
12
【样例说明】
下面是对样例1的说明。
以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:
1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23
【数据规模和约定】
设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
对于100%的数据,1≤n≤109,1≤m≤5,1≤pi^ci≤10^5。
【样例说明】
下面是对样例1的说明。
以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:
1/23 1/24 1/34
2/13 2/14 2/34
3/12 3/14 3/24
4/12 4/13 4/23
【数据规模和约定】
设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
对于100%的数据,1≤n≤109,1≤m≤5,1≤pi^ci≤10^5。
HINT
Source
思考
终于学了扩展lucas定理,感觉对组合数有了更深的认识。
解决n>10^9,p<=1e5时用这个方法挺好的。
想清楚后感觉code的实现挺暴力的。
核心并不是放在利用CRT合并形如
Cmn
这样的式子上。
对于
Cmn
来说真正重要的不是怎么求(那么多种方法。。。),而是解决p不是质数上。
p=pcii∗pcjj∗...∗pckk
,
考虑拆分法则:
我们现在可以利用CRT合并:
Cmini≡bi(modpcii)
Cmjnj≡bj(modpcjj)
.......
Cmknk≡bk(modpckk)
这样的式子,那么扩展卢卡斯的解决范围也就确定了!
解决形如:
Cmini≡bi(modpcii) 的式子。
考虑到 Cmn=n!m!(n−m)! ,只要我们能够分别计算出……即形如: x!≡bi(modpki) 的式子即可。
可知: n! 中质因子p的个数x的公式为 x=⌊np⌋+⌊np2⌋+⌊np3⌋+...
递推式也可以写为 f(n)=f(⌊np2⌋)+⌊np2⌋
又因为 n! 可表示为:
n!=1∗2∗3∗...∗n=(1∗2∗3∗...∗n(i∉(x≡0(modp))))∗(p⌊np⌋)∗(1∗2∗...∗⌊np⌋)
(p⌊np⌋)∗(1∗2∗...∗⌊np⌋)
在下有模
pk
的循环节。
于是乎,暴力提取
m!、n!、(n−m)!
中质因子pi并消去,求出逆元inv后再全部乘回去。
参考代码
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void ex_gcd(int a,int b,int &x,int &y){
if(b==0){x=1;y=0;return ;}
ex_gcd(b,a%b,x,y);
int t=x;x=y;y=t-a/b*x;
}
int pow_mod(long long x,long long y,long long p){
long long ret=1;
while(y){
if(y&1)ret=(ret*x)%p;
x=(x*x)%p;y>>=1;
}
return ret;
}
int inv(int a,int p){
if(!a)return 0;
int x=0,y=0;
ex_gcd(a,p,x,y);
x=(x%p+p)%p;
return x==0?p:x;
}
int Mul(int n,int pi,int pk){
if(!n)return 1;
long long ret=1;
for(register int i=2;i<=pk;i++)if(i%pi)ret=ret*i%pk;
ret=pow_mod(ret,n/pk,pk);
for(register int i=2;i<=n%pk;i++)if(i%pi)ret=ret*i%pk;
return ret*Mul(n/pi,pi,pk)%pk;
}
int C(int n,int m,int p,long long pi,long long pk){
if(m>n)return 0;
long long a=Mul(n,pi,pk),b=Mul(m,pi,pk),c=Mul(n-m,pi,pk);
long long k=0,ret;
for(register int i=n;i;i/=pi)k+=i/pi;
for(register int i=m;i;i/=pi)k-=i/pi;
for(register int i=n-m;i;i/=pi)k-=i/pi;
ret=a*inv(b,pk)%pk*inv(c,pk)%pk*pow_mod(pi,k,pk)%pk;
return (int)(ret*(p/pk)%p*inv(p/pk,pk)%p);
}
int p,n,m,d,w[30],M;
long long ans=1,sum;
int main(){
scanf("%d%d%d",&p,&n,&m);
for(register int i=1;i<=m;i++)
scanf("%d",&w[i]),sum+=w[i];
if(sum>n)return puts("Impossible"),0;
for(register long long P,ret,i=1;i<=m;i++){
n-=w[i-1],P=p,ret=0;
for(register int j=2;j*j<=P;j++){
if(P%j==0){
int px=1;
while(P%j==0)px*=j,P/=j;
ret=(ret+(C(n,w[i],p,j,px))%p)%p;
}
}
if(P>1)ret=(ret+(C(n,w[i],p,P,P))%p)%p;
ans=ans*ret%p;
}
printf("%lld",ans);
return 0;
}