“卢卡斯定理:C(n,m)=C(n%p,m%p)*C(n/p,m/p) mod p 要求p是质数
其中n%p可能会小于m%p 这种情况下直接返回0即可
证明去问卢卡斯 我不知道”
——PoPoQQQ爷
http://blog.csdn.net/popoqqq/article/details/40507373
”题目大意:给定n个物品,分给m个人,每个人拿到wi个礼物,问方案数mod P P不一定为质数
首先我们把剩下的礼物也分给一个人 答案明显不变 w[++m]=n-w1-w2-...-wm
然后就会很方便地得到公式:
ans=C(n,w1)*C(n-w1,w2)*C(n-w1-w2,w3)*...*C(n-w1-w2-...-w_(m-1),wm) mod P
=n!/w1!/w2!/.../wm! mod P
然后p不是质数 我们把P分解 令P=∏pi^ai
我们分别处理,可以得到一次同余方程组ans%pi^ai=lefti,用中国剩余定理合并一下即可。
然后对于每个pi^ai,我们进行以下处理:
将分子和分母化为x*pi^y的形式
然后分母的x部分与pi互质,可以求逆元,分子分母的y部分直接相减即可
然后我们处理阶乘
以19为例,将19!化为x*pi^y的形式,其中pi=3,ai=2 则有
19!%9=(1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19) %9
=(1*2*4*5*7*8*10*11*13*14*16*17*19)*3^6*(1*2*3*4*5*6) %9
式子的左半部分是不为3的倍数的数,存在长度为p^a的循环节 求出一个循环节 快速幂处理 然后处理剩余部分
右半部分是6!%9 递归处理即可
我这题解写的真是冷静。。。这题还真TM让人冷静不下来啊-0-“
——PoPoQQQ爷
http://blog.csdn.net/popoqqq/article/details/39891263
19!%9 这个例子特别好
n!/w1!/w2!/.../wm! mod P
然后中国剩余定理合并
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<iostream> using namespace std; typedef long long ll; typedef pair<ll,ll> data; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(ll &x) { char c=nc(),b=1; for (; !(c>='0' && c<='9'); c=nc()) if (c=='-') b=-1; for (x=0; c>='0' && c<='9'; x=x*10+c-'0',c=nc()); x*=b; } inline ll Mul(ll a,ll b,ll p) { ll ret=0; for (;b;b>>=1,a=(a<<1)%p) if (b&1) (ret+=a)%=p; return ret; } inline ll Pow(ll a,ll b,ll p) { if (b==0) return 1LL; ll ret=Pow(a,b>>1,p); if (b&1) return ret*ret%p*a%p; return ret*ret%p; } inline ll EXGCD(ll a,ll b,ll& x,ll &y) //a>=b { if (a<b) return EXGCD(b,a,y,x); if (b==0) { x=1; y=0; return a; } ll d=EXGCD(b,a%b,x,y); ll tmp=y; y=x-a/b*y; x=tmp; return d; } inline ll Reverse(ll a,ll p) { ll X,Y; EXGCD(a,p,X,Y); return (X%p+p)%p; } namespace China { ll A[100005],N[100005],C[100005]; int tot=0; ll P=1; inline void add(ll a,ll n) { A[++tot]=a; N[tot]=n; P*=n; } inline ll solve() { ll X,Y,ret=0,tmp; for (int i=1; i<=tot; i++) { C[i]=P/N[i]; X=Reverse(C[i],N[i]); (ret+=Mul(Mul(A[i],C[i],P),X,P))%=P; } return ret; } } int vst[100005],prime[100005],num; inline void Pre() { const int maxn=100000; for (int i=2; i<=maxn; i++) if (!vst[i]) { prime[++num]=i; for (int j=i+i; j<=maxn; j+=i) vst[j]=1; } } ll M,ans; ll p[50005],a[50005],p_a[50005],cnt; ll n,m,w[10]; inline void Machine(ll M) { for (int i=1; i<=num; i++) if (M%prime[i]==0) { p[++cnt]=prime[i]; while (M%prime[i]==0) a[cnt]++,M/=prime[i]; p_a[cnt]=Pow(p[cnt],a[cnt],1000000); } } inline data Calc(ll x,ll p,ll a,ll p_a) { ll A=1,B=1; for (ll i=1;i<p_a;i++) if (i%p) (A*=i)%=p_a; A=Pow(A,x/p_a,p_a); for (ll i=x-x%p_a+1;i<=x;i++) if (i%p) (A*=i)%=p_a; B=x/p; if (B) { data tmp=Calc(B,p,a,p_a); (A*=tmp.first)%=p_a; B+=tmp.second; } return make_pair(A,B); } int main() { Pre(); freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(M); Machine(M); read(n); read(m); ll sum=0; for (int i=1; i<=m; i++) read(w[i]),sum+=w[i]; if (sum>n) printf("Impossible\n"),exit(0); if (n-sum) w[++m]=n-sum; for (int i=1;i<=cnt;i++) { data ret=Calc(n,p[i],a[i],p_a[i]); for (int j=1;j<=m;j++) { data tmp=Calc(w[j],p[i],a[i],p_a[i]); ret.second-=tmp.second; (ret.first*=Reverse(tmp.first,p_a[i]))%=p_a[i]; } (ret.first*=Pow(p[i],ret.second,p_a[i]))%=p_a[i]; China::add(ret.first,p_a[i]); } ans=China::solve(); cout<<ans<<endl; return 0; }
去0时记得乘上2,5的逆元
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<iostream> #include<iomanip> #define P1 512ll #define P2 1953125ll using namespace std; typedef long long ll; typedef pair<ll,ll> data; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(ll &x) { char c=nc(),b=1; for (; !(c>='0' && c<='9'); c=nc()) if (c=='-') b=-1; for (x=0; c>='0' && c<='9'; x=x*10+c-'0',c=nc()); x*=b; } inline ll Pow(ll a,ll b,ll p) { if (b==0) return 1LL; ll ret=Pow(a,b>>1,p); if (b&1) return ret*ret%p*a%p; return ret*ret%p; } inline data Plus(data x,data y,ll p) { return make_pair(x.first*y.first%p,x.second+y.second); } inline data Minus(data x,data y,ll p,ll phi_p) { return make_pair(x.first*Pow(y.first,phi_p-1,p)%p,x.second-y.second); } inline data Find(ll x,ll p,ll p_a,ll* pow) { if (!x) return make_pair(1,0); ll tmp=Pow(pow[p_a],x/p_a,p_a)*pow[x%p_a]%p_a; return Plus(make_pair(tmp,x/p),Find(x/p,p,p_a,pow),p_a); } ll n,m,K,ans; ll pow1[P1+1],pow2[P2+1]; int main() { data ret1,ret2; ll X,Y,tmp; freopen("t.in","r",stdin); freopen("t.out","w",stdout); pow1[0]=1; pow2[0]=1; for (int i=1;i<=P1;i++) pow1[i]=pow1[i-1]*(i%2?i:1)%P1; for (int i=1;i<=P2;i++) pow2[i]=pow2[i-1]*(i%5?i:1)%P2; read(n); read(m); read(K); ret1=Find(n+m,2,P1,pow1); ret1=Minus(ret1,Find(n,2,P1,pow1),P1,P1/2*1); ret1=Minus(ret1,Find(m,2,P1,pow1),P1,P1/2*1); ret2=Find(n+m,5,P2,pow2); ret2=Minus(ret2,Find(n,5,P2,pow2),P2,P2/5*4); ret2=Minus(ret2,Find(m,5,P2,pow2),P2,P2/5*4); tmp=min(ret1.second,ret2.second); X=ret1.first*Pow(2,ret1.second-tmp,P1)%P1*Pow(205,tmp,P1)%P1; Y=ret2.first*Pow(5,ret2.second-tmp,P2)%P2*Pow(976563,tmp,P2)%P2; ans=(1953125*109*X+512*1537323*Y)%1000000000; cout<<setfill('0')<<setw(K)<<ans%Pow(10,K,1000000001)<<endl; return 0; }
Lucas定理
逆元预处理 (inv[i]=(P-P/i)*inv[P%i])%=P
#include<cstdio> #include<cstdlib> #define M 10007 using namespace std; typedef long long ll; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(ll &x) { char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } inline void write(ll x,char c) { if (x==0) { putchar('0'); putchar(c); return; } if (x<0) putchar('-'),x=-x; char s[50]={0},len=0; while (x) s[++len]=x%10+'0',x/=10; for (int i=len;i;i--) putchar(s[i]); putchar(c); } ll fac[10010],inv[10010]; inline void Pre() { fac[0]=1; for (int i=1;i<M;i++) fac[i]=fac[i-1]*i%M; inv[1]=1; for (int i=2;i<M;i++) inv[i]=(M-M/i)*inv[M%i]%M; inv[0]=1; for (int i=1;i<M;i++) (inv[i]*=inv[i-1])%=M; } inline ll C(ll n,ll m) { if (m>n) return 0LL; if (n<M && m<M) return fac[n]*inv[m]%M*inv[n-m]%M; return C(n/M,m/M)*C(n%M,m%M)%M; } int main() { Pre(); ll Q,n,m; freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(Q); while (Q--) { read(n); read(m); write(C(n,m),'\n'); } return 0; }
还得用上费马小定理
“注意此题有个细节 就是欧拉定理中a与p必须互质 而当a=0(即G=p)时gcd(a,p)=p
所以有一组数据是专门卡这个地方的 这组数据是999911657 999911659 取模后是0^0 于是得到1 但是答案是0 ” ——PoPoQQQ爷
#include<cstdio> #include<cstdlib> #include<algorithm> #include<iostream> #define M 999911659LL using namespace std; typedef long long ll; typedef pair<ll,ll> data; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(ll &x) { char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } ll prime[4]={2,3,4679,35617}; ll fac[4][36000],inv[4][36000]; inline void Pre() { for (int i=0;i<4;i++) { ll P=prime[i]; fac[i][0]=1; for (int j=1;j<=P;j++) (fac[i][j]=j*fac[i][j-1])%=P; inv[i][1]=1; for (int j=2;j<=P;j++) (inv[i][j]=(P-P/j)*inv[i][P%j])%=P; inv[i][0]=1; for (int j=1;j<=P;j++) (inv[i][j]*=inv[i][j-1])%=P; } } inline ll C(ll n,ll m,ll P,int t) { if (n<m) return 0LL; if (n<P && m<P) return fac[t][n]*inv[t][m]%P*inv[t][n-m]%P; return C(n/P,m/P,P,t)*C(n%P,m%P,P,t)%P; } inline data EXGCD(ll a,ll b) { if (a<b) { data tmp=EXGCD(b,a); return data(tmp.second,tmp.first); } if (b==0) return data(1,0); data tmp=EXGCD(b,a%b); return data(tmp.second,tmp.first-a/b*tmp.second); } inline ll Chinese(ll *A,ll *N,ll P,int cnt) { ll ret=0,Mi,Ci; for (int i=0;i<cnt;i++) { Mi=P/N[i]; Ci=(EXGCD(Mi,N[i]).first%P*Mi%P+P)%P; (ret+=Ci*A[i]%P)%=P; } return ret; } inline ll Pow(ll a,ll b,ll p) { if (b==0) return 1LL; ll ret=Pow(a,b>>1,p); if (b&1) return ret*ret%p*a%p; return ret*ret%p; } ll N,G,Ans; ll ans[4]; inline void Calc(ll x) { for (int i=0;i<4;i++) (ans[i]+=C(N,x,prime[i],i))%=prime[i]; } int main() { Pre(); freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(N); read(G); for (int i=1;i*i<=N;i++) if (i*i==N) Calc(i); else if (N%i==0) Calc(i),Calc(N/i); Ans=Chinese(ans,prime,M-1,4); Ans=Pow(G%M,Ans+M-1,M); cout<<Ans<<endl; }
组合数dp
#include<cstdio> #include<cstdlib> #include<algorithm> #include<iostream> #define X first #define Y second using namespace std; typedef long long ll; typedef pair<ll,ll> data; typedef pair<ll,ll> Poi; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(ll &x) { char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } namespace Deal1{ #define M 1019663265LL ll prime[4]={3,5,6793,10007}; ll fac[4][36000],inv[4][36000]; inline void Pre() { for (int i=0;i<4;i++) { ll P=prime[i]; fac[i][0]=1; for (int j=1;j<=P;j++) (fac[i][j]=j*fac[i][j-1])%=P; inv[i][1]=1; for (int j=2;j<=P;j++) (inv[i][j]=(P-P/j)*inv[i][P%j])%=P; inv[i][0]=1; for (int j=1;j<=P;j++) (inv[i][j]*=inv[i][j-1])%=P; } } inline ll C(ll n,ll m,ll P,int t) { if (n<m) return 0LL; if (n<P && m<P) return fac[t][n]*inv[t][m]%P*inv[t][n-m]%P; return C(n/P,m/P,P,t)*C(n%P,m%P,P,t)%P; } inline data EXGCD(ll a,ll b) { if (b==0) return data(1,0); data tmp=EXGCD(b,a%b); return data(tmp.second,tmp.first-a/b*tmp.second); } inline ll Chinese(ll *A,ll *N,ll P,int cnt) { ll ret=0,Mi,Ci; for (int i=0;i<cnt;i++) { Mi=P/N[i]; Ci=(EXGCD(Mi%N[i],N[i]).first%P*Mi%P+P)%P; (ret+=Ci*A[i]%P)%=P; } return ret; } ll Ans,ans[4]; inline void Calc(ll n,ll m) { for (int i=0;i<4;i++) (ans[i]=C(n,m,prime[i],i))%=prime[i]; } inline ll Solve(ll n,ll m) { Calc(n,m); Ans=Chinese(ans,prime,M,4); return Ans; } #undef M } namespace Deal2{ ll P=1000003; ll fac[1000005],inv[1000005]; inline void Pre() { fac[0]=1; for (int j=1;j<=P;j++) (fac[j]=j*fac[j-1])%=P; inv[1]=1; for (int j=2;j<=P;j++) (inv[j]=(P-P/j)*inv[P%j])%=P; inv[0]=1; for (int j=1;j<=P;j++) (inv[j]*=inv[j-1])%=P; } inline ll C(ll n,ll m) { if (n<m) return 0LL; if (n<P && m<P) return fac[n]*inv[m]%P*inv[n-m]%P; return C(n/P,m/P)*C(n%P,m%P)%P; } inline ll Solve(ll n,ll m) { return C(n,m); } } ll N,M,T,P; Poi point[205]; ll f[205]; inline ll CC(ll n,ll m) { ll ret; if (P==1000003) ret=Deal2::Solve(n,m); else ret=Deal1::Solve(n,m); return ret; } int main() { freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(N); read(M); read(T); read(P); if (P==1000003) Deal2::Pre(); else Deal1::Pre(); for (int i=1;i<=T;i++) read(point[i].X),read(point[i].Y); point[++T]=Poi(N,M); sort(point+1,point+T+1); for (int i=1;i<=T;i++) { f[i]=CC(point[i].X+point[i].Y,point[i].X); for (int j=1;j<i;j++) if (point[i].Y>=point[j].Y) ((f[i]-=f[j]*CC((point[i].X-point[j].X)+(point[i].Y-point[j].Y),(point[i].X-point[j].X))%P)+=P)%=P; } cout<<f[T]<<endl; return 0; }
一开始没看出是个小根堆。。组合数dp
#include<cstdio> #include<cstdlib> #include<iostream> using namespace std; typedef long long ll; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; } return *p1++; } inline void read(ll &x) { char c=nc(),b=1; for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; } ll n,P; ll fac[1000005],inv[1000005]; ll f[2000005],size[2000005]; inline void Pre() { fac[0]=1; for (int i=1;i<=n && i<P;i++) (fac[i]=fac[i-1]*i)%=P; inv[1]=1; for (int i=2;i<=n && i<P;i++) (inv[i]=(P-P/i)*inv[P%i])%=P; inv[0]=1; for (int i=1;i<=n && i<P;i++) (inv[i]*=inv[i-1])%=P; } inline ll C(ll n,ll m) { if (n<m) return 0LL; if (n<P && m<P) return fac[n]*inv[m]%P*inv[n-m]%P; return C(n/P,m/P)*C(n%P,m%P)%P; } int main() { freopen("t.in","r",stdin); freopen("t.out","w",stdout); read(n); read(P); Pre(); for (int i=n+1;i<=(n<<1|1);i++) f[i]=1; for (int i=n;i;i--) { size[i]=size[i<<1]+size[i<<1|1]+1; f[i]=C(size[i]-1,size[i<<1])*f[i<<1]%P*f[i<<1|1]%P; } cout<<f[1]<<endl; }