2142: 礼物
Time Limit: 10 Sec Memory Limit: 259 MB
Submit: 701 Solved: 283
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
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。
组合数取模终极题目。
这道题的答案很好算C(n,tot)+C(tot,w[1])+C(tot-w[1],w[2])+...
由于n很大,想到用lucas定理,但是模不是质数!!
首先质数的幂之间一定互质,那么我们可以把模分解成几个质数的幂的乘积的形式,对每一个质数的幂求解,然后用中国剩余定理将他们合并起来即可。
那么如何快速计算组合数对质数的幂取模呢?
C(n,m)=n!/(m!*(n-m)!)
我们可以把分子分母分别化成t1*p^u1,t2*p^u2的形式,然后把C(n,m)=t1/t2 *p^(u1-u2),此时t1/t2可以用扩展欧几里得直接求出,乘上p^(u1-u2)就是答案。
可是如何快速求出n!=t*p^u的t和u的值呢?
我们把所有是p的倍数的数提出来,先不考虑。
先考虑t的值(注意所有是p的倍数的数不参与计算):
如果y=n/(p^u),如果y>1那么我们可以算出1-p^u的值为ans,那么t=ans^y*剩下数的乘积,否则直接暴力算1-n
再看u的值,p的倍数的数有1*p,2*p,3*p.....k*p(k=n/p),我们把p提出来之后就变成了1,2,3...k,于是问题变成了更小规模的原问题!递归解决即可~
复杂度是线性的~
#include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <cstdlib> #include <cstdio> #define N 100000+5 #define LL long long #define pa pair<LL,LL> #define mp make_pair #define fi first #define se second using namespace std; LL mod,a[N]; int n,m,v[N],p[N],s[100],tot,num; struct divi { int p,c,pc; }w[N]; void Prepare() { int cnt=0; for (int i=2;i<=100000;i++) if (!v[i]) { p[++cnt]=i; for (int j=i;j<=100000;j+=i) v[j]=1; } num=0; LL x=mod; for (int i=1;i<=cnt;i++) { if (x<=1) break; if (x%p[i]) continue; w[++num].p=p[i]; w[num].pc=1; while (x%p[i]==0) w[num].c++,x/=p[i],w[num].pc*=p[i]; } } LL Pow(LL a,int n,LL mod) { LL base=a,ans=1; while (n) { if (n&1) ans=ans*base%mod; base=base*base%mod; n>>=1; } return ans; } void Exgcd(LL a,LL b,LL &d,LL &x,LL &y) { if (!b) { d=a,x=1,y=0; return; } Exgcd(b,a%b,d,y,x); y-=x*(a/b); } LL Inv(LL a,LL n) { LL xx,yy,d; Exgcd(a,n,d,xx,yy); return (xx%n+n)%n; } pa Fac(int k,LL n) { if (n==0) return mp(0,1); int x=n/w[k].p,y=n/w[k].pc; LL ans=1; if (y) { for (int i=2;i<w[k].pc;i++) if (i%w[k].p!=0) ans=ans*1LL*i%w[k].pc; ans=Pow(ans,y,w[k].pc); } for (int i=y*w[k].pc+1;i<=n;i++) if (i%w[k].p!=0) ans=ans*1LL*i%mod; pa p=Fac(k,x); return mp(x+p.fi,ans*p.se%mod); } LL Get(int k,LL n,LL m) { if (n<m) return 0; pa a=Fac(k,n),b=Fac(k,m),c=Fac(k,n-m); LL mod=w[k].pc; return Pow(w[k].p,a.fi-b.fi-c.fi,mod)* a.se%mod*Inv(b.se,mod)%mod* Inv(c.se,mod)%mod; } LL CRT() { LL M=mod,d,y,x=0; for (int i=1;i<=num;i++) { LL r=M/w[i].pc; Exgcd(w[i].pc,r,d,d,y); x=(x+r*y*a[i])%M; } return (x+M)%M; } LL C(int n,int m) { for (int i=1;i<=num;i++) a[i]=Get(i,n,m); return CRT(); } int main() { scanf("%lld",&mod); scanf("%d%d",&n,&m); Prepare(); LL ans; int tot=0; for (int i=1;i<=m;i++) scanf("%d",&s[i]),tot+=s[i]; if (tot>n) { puts("Impossible"); return 0; } ans=C(n,tot)%mod; for (int i=1;i<=m;i++) { ans=ans*C(tot,s[i])%mod; tot-=s[i]; } printf("%lld\n",ans); return 0; }