bzoj 2142
数论大集合
只要你做完了这道题,除了线性筛和降幂公式以外,所有数论noip知识点就都会了...
题意:求C(n,∑w)*C(∑w,w1)*C(∑w-w1,w2).....mod p(不保证p为质数)
思想:拓展卢卡斯定理
算法:我们可以分别求每个C(n,m),然后乘起来mod p即可
在求每个C(n,m)时,由公式C(n,m)=
于是:C(n,m)==
于是我们仅需求出n!mod p的值
可是首先,由于p不是质数,所以不能线性筛逆元
而且,即使p是质数,由于n的范围过大,筛出来也T了
所以我们要采用一些别的方法:
设
于是我们可以求出原式模每个的值(或逆元)
然后应用中国剩余定理合并
这就解决了第一个问题
至于第二个问题,我们举例来说明一下:
求19!mod 9的值(经典样例,来自popoqqq)
19!mod 9=19*18*17*16...*1mod 9=(1*2*4*5*7*8*10*11*13*14*16*17*19)mod 9*3^6 mod 9*(1*2*3*4*5*6) mod 9
对于第一部分,显然,1,2,4,5,7,8和10,11,13,14,16,17会构成一个对9的剩余系,那么这堆东西可以用快速幂来做,因为就是一个剩余系
对于第二部分,我们仅需记录一下幂次,然后除掉下面的再处理(别忘了C(n,m)是有除法的,算出来取完模一堆0好像不太妙啊...)
对于第三部分,发现还是一个阶乘,那我们递归处理即可
最后,用中国剩余定理合并,就完事了
代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #define ll long long using namespace std; ll w[10]; ll n,m,p; ll p0[25],pu[25],nu[25],cnt; ll ret[25]; ll a[25]; ll tot=0; struct node { ll mi; ll val; }; void destroy() { int temp=p; for(int i=2;i*i<=temp;i++) { if(temp%i==0) { p0[++cnt]=i; pu[cnt]=i; nu[cnt]=1; temp/=i; while(temp%i==0) { pu[cnt]*=i; nu[cnt]++; temp/=i; } } } if(temp!=1) { p0[++cnt]=temp; pu[cnt]=temp; nu[cnt]=1; } } ll gcd(ll x,ll y) { if(y==0) { return x; } return gcd(y,x%y); } void ex_gcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return; } ex_gcd(b,a%b,x,y); ll t=x; x=y; y=t-(a/b)*x; } ll get_inv(ll x,ll y) { ll xx,yy; ex_gcd(x,y,xx,yy); return ((xx%y)+y)%y; } ll pow_mul(ll x,ll y,ll mod) { ll ans=1; while(y) { if(y%2) { ans*=x; ans%=mod; } x*=x; x%=mod; y/=2; } return ans; } node getmul(ll x,ll num) { if(x==0) { node ret; ret.mi=0; ret.val=1; return ret; } ll ans=1; ll p1=x/p0[num],p2=x/pu[num]; if(p2) { for(int i=2;i<pu[num];i++) { if(i%p0[num]) { ans*=i; ans%=pu[num]; } } ans=pow_mul(ans,p2,pu[num]); } for(int i=p2*pu[num]+1;i<=x;i++) { if(i%p0[num]) { ans*=i; ans%=p; } } node re=getmul(p1,num); node temp; temp.mi=re.mi+x; temp.val=ans*re.val%p; return temp; } ll C(ll x,ll y,ll num)//C(x,y)=x!/(y!(x-y)!) { if(x<y) { return 0; } node f1=getmul(x,num); node f2=getmul(y,num); node f3=getmul(x-y,num); ll t1=pow_mul(p0[num],f1.mi-f2.mi-f3.mi,pu[num])%pu[num]; ll t2=f1.val*get_inv(f2.val,pu[num])%pu[num]; ll t3=get_inv(f3.val,pu[num])%pu[num]; return t1*t2%pu[num]*t3%pu[num]; } ll china() { ll M=p; ll ans=0; for(int i=1;i<=cnt;i++) { ll M0=M/pu[i]; ll xx,yy; ex_gcd(M0,pu[i],xx,yy); ans+=xx*M0%p*a[i]%p; ans%=p; } return (ans%M+M)%M; } ll solve(ll x,ll y) { for(int i=1;i<=cnt;i++) { a[i]=C(x,y,i); } return china(); } int main() { scanf("%lld%lld%lld",&p,&n,&m); ll s=0; for(int i=1;i<=m;i++) { scanf("%d",&w[i]); s+=w[i]; } if(s>n) { printf("Impossible\n"); return 0; } destroy(); ll re=solve(n,s); for(int i=1;i<=m;i++) { re*=solve(s,w[i]); re%=p; s-=w[i]; } printf("%lld\n",re); return 0; }