1. 程式人生 > >[國家集訓隊] 禮物

[國家集訓隊] 禮物

洛谷 P2183 傳送門

bzoj 2142 傳送門

一共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 }