[国家集训队] 礼物
一共n个礼物,分给m个人,每个人分wi个。
如果不够分,当然是Impossible。
如果够分,考虑求出1-n的所有排列,然后前w1个分给第一个人,w2个分给第二个人......
剩下的那些,当做分给了第m+1个人。
这样共有 n! 种排列。
但是每个人(包括第m+1个)分到的礼物只要本质一样就行,排列顺序无所谓。
所以我们再除掉 w1!、w2!、...、wm+1! 就行了。
取模数为非质数,用扩展卢卡斯的方法计算阶乘即可。
最开始全WA,调了一下发现是逆元求错了......求x逆元的函数会return x......
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 7 int n,m; 8 ll p; 9 ll w[8]; 10 ll ans; 11 12 ll ksm(ll bs,ll tp,ll mod) 13 { 14 ll ret=1; 15 while(tp) 16 { 17 if(tp&1)ret=ret*bs%mod; 18 bs=bs*bs%mod; 19 tp>>=1; 20 } 21 return ret; 22 } 23 24 ll exgcd(ll a,ll b,ll &x,ll &y) 25 { 26 if(!b) 27 { 28 x=1;y=0; 29 return a; 30 } 31 ll ret=exgcd(b,a%b,y,x); 32 y-=a/b*x; 33 return ret; 34 } 35 36 ll inv(ll x,ll mod) 37 { 38 ll ret,tmp; 39 exgcd(x,mod,ret,tmp); 40 return (ret%mod+mod)%mod; 41 } 42 43 ll crt(ll a,ll pk) 44 { 45 return a*(p/pk)%p*inv(p/pk,pk)%p; 46 } 47 48 ll fac(ll x,ll pi,ll pk) 49 { 50 if(!x)return 1; 51 ll ret=1; 52 for(ll i=2;i<=pk;i++) 53 if(i%pi)ret=ret*i%pk; 54 ret=ksm(ret,x/pk,pk); 55 for(ll i=2;i<=x%pk;i++) 56 if(i%pi)ret=ret*i%pk; 57 return ret*fac(x/pi,pi,pk)%pk; 58 } 59 60 void cal(ll pi,ll pk) 61 { 62 ll cnt=0; 63 ll up=fac(n,pi,pk); 64 for(ll i=n;i;i/=pi)cnt+=i/pi; 65 ll down=1; 66 for(int j=1;j<=m;j++) 67 { 68 down=down*fac(w[j],pi,pk)%pk; 69 for(ll i=w[j];i;i/=pi)cnt-=i/pi; 70 } 71 ll tmp=up*inv(down,pk)%pk*ksm(pi,cnt,pk)%pk; 72 ans=(ans+crt(tmp,pk))%p; 73 } 74 75 int main() 76 { 77 scanf("%lld",&p); 78 scanf("%d%d",&n,&m); 79 w[m+1]=(ll)n; 80 for(int i=1;i<=m;i++)scanf("%lld",&w[i]),w[m+1]-=w[i]; 81 m++; 82 if(w[m]<0)return printf("Impossible"),0; 83 ll tp=p; 84 for(ll i=2;i*i<=p;i++) 85 { 86 if(tp%i)continue; 87 ll pk=1; 88 while(tp%i==0)tp/=i,pk*=i; 89 cal(i,pk); 90 } 91 if(tp>1)cal(tp,tp); 92 printf("%lld",ans); 93 return 0; 94 }