大組合數取模
考慮C(n,m)%P
情況一:n,m很大,P為素數
處理小範圍的階乘和階乘的逆元 用盧卡斯定理即可。盧卡斯定理:
情況二: 當P=
求出
得到模方程組
程式碼如下:
#include<cstdio>
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<cmath>
#define LL long long
const LL M= 999911659;
const int maxn= 50000+5;
using namespace std;
template <typename T>
inline void _read(T &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0 ;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
LL P[4]= {2,3,4679,35617};
LL N,G,fra[maxn][4],inv[maxn][4];
LL ans[4];
LL pow_mod(LL x,LL y,LL M){
LL ans=1;
for(x%=M;y;y>>=1,x=x*x%M)
if(y&1) ans=ans*x%M;
return ans;
}
// 解模方程組 x=A[i] (moc m[i]) 0<=i<n
void EXGCD(LL A,LL B,LL& d,LL& x,LL& y){
if(B==0){d=A;x=1;y=0;}
else {EXGCD(B,A%B,d,y,x); y-= x*(A/B);}
}
LL China(LL * A,LL * m,LL n){
LL M= 1,ret=0,i;
for(i=0;i<n;i++)M*= m[i];
for(i=0;i<n;i++){
LL d,x,y,t= M/m[i];
EXGCD(m[i],t,d,x,y);
ret= (ret+ A[i]*y*t%M)%M;
}
return (ret+M)%M;
}
LL C(LL n,LL m,LL id){
if(m>n) return 0;
if(!m||n==m) return 1;
if(n<=P[id] && m<=P[id])
return fra[n][id]*inv[m][id]%P[id]*inv[n-m][id]%P[id];
return C(n/P[id],m/P[id],id)*C(n%P[id],m%P[id],id)%P[id];
}
void Divide(LL n){
LL i,j;
for(i=1;i*i<=n;i++){
if(n%i!=0) continue;
for(j=0;j<4;j++){
ans[j]= ans[j]+C(n,i,j);
if(ans[j]>=P[j]) ans[j]-=P[j];
}
if(i*i!=n){
for(j=0;j<4;j++){
ans[j]= ans[j]+ C(n,n/i,j);
if(ans[j]>=P[j]) ans[j]-=P[j];
}
}
}
}
void Init(){
LL i,j;
for(i=0;i<4;i++){ //分別預處理每一個質數
LL mod= P[i];
inv[1][i]= fra[1][i]=1;
inv[0][i]= fra[0][i]=1;
for(j=2;j<=P[i];j++){
fra[j][i]= fra[j-1][i]*j%mod;
inv[j][i]= (mod-mod/j)*inv[mod%j][i]%mod;
}
for(j=2;j<=P[i];j++) inv[j][i]= inv[j][i]*inv[j-1][i]%mod;
}
}
int main(){
cin>>N>>G;
Init();
Divide(N);
//for(int i=0;i<4;i++) cout<<ans[i]<<" ";cout<<endl;
LL exp= China(ans,P,4);
if(exp==0) exp= M-1;
cout<<pow_mod(G,exp,M)<<endl;
return 0;
}
/*
900951975 90523153
81793986
*/
終極情況:C(n,m)%p
n,m非常大 -> 盧卡斯定理
p=p1^c1 * p2^c2 * … * pn^cn
由之前的情況我們已經可以得到了一些結論,這種情況怎麼辦?
同理 盧卡斯定理 + CRT
但是前提條件是要求 C(n,m)% pi^ci
C(n,m)% pi^ci怎麼求?
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define ll long long
using namespace std;
const ll label=1000000000000000LL;
template <typename T>
inline void _read(T& x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
ll mont(ll x,ll y,ll z){
ll temp=1;
for(x%=z;y;y>>=1,x=x*x%z){
if(y&1)temp=temp*x%z;
}
return temp;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;y=0;return a;
}
else{
ll ans=exgcd(b,a%b,x,y);
ll t=x;
x=y;y=t-a/b*y;return ans;
}
}
ll rev(ll a,ll b){
ll x,y;
exgcd(a,b,x,y);
return (x%b+b)%b;
}
ll C(ll n,ll m,ll mod){
if(m>n)return 0;
ll ans=1,i,a,b;
for(i=1;i<=m;i++){
a=(n+1-i)%mod;
b=rev(i%mod,mod);
ans=ans*a%mod*b%mod;
}
return ans;
}
ll C1(ll n,ll m,ll mod){
if(m==0)return 1;
return C(n%mod,m%mod,mod)*C1(n/mod,m/mod,mod)%mod;
}
ll cal(ll n,ll p,ll t){
if(!n)return 1;
ll x=mont(p,t,label),i,y=n/x,temp=1;
for(i=1;i<=x;i++){
if(i%p)temp=temp*i%x;
}
ll ans=mont(temp,y,x);
for(i=y*x+1;i<=n;i++){
if(i%p)ans=ans*i%x;
}
return ans*cal(n/p,p,t)%x;
}
ll C2(ll n,ll m,ll p,ll t){
ll x=mont(p,t,label);
ll a,b,c,ap=0,bp=0,cp=0,temp;
for(temp=n;temp;temp/=p)ap+=temp/p;
for(temp=m;temp;temp/=p)bp+=temp/p;
for(temp=n-m;temp;temp/=p)cp+=temp/p;
ap=ap-bp-cp;
ll ans=mont(p,ap,x);
a=cal(n,p,t);
b=cal(m,p,t);
c=cal(n-m,p,t);
ans=ans*a%x*rev(b,x)%x*rev(c,x)%x;
return ans;
}
ll CRT(ll A[],ll B[],ll r){
ll M=1;
ll i,j,k,ans=0,d,t,x0,y0;
for(i=1;i<=r;i++){
M*=A[i];
}
for(i=1;i<=r;i++){
d=M/A[i];
exgcd(d,A[i],x0,y0);
ans=(ans+d*x0*B[i])%M;
}
while(ans<=0)ans+=M;
return ans;
}
ll lucas(ll x,ll y,ll mod){
ll i,j,k,d,cnt=0,t;
ll A[205],M[205];
for(i=2;i*i<=mod;i++){
if(mod%i==0){
t=0;
while(mod%i==0){
mod/=i;
t++;
}
M[++cnt]=mont(i,t,label);
if(t==1)A[cnt]=C1(x,y,i);
else A[cnt]=C2(x,y,i,t);
}
}
if(mod>1){
M[++cnt]=mod;
A[cnt]=C1(x,y,mod);
}
/*cout<<"divide:"<<endl;
for(i=1;i<=cnt;i++){
cout<<"M:"<<M[i]<<" residue:"<<A[i]<<endl;
}*/
return CRT(M,A,cnt);
}
int T;
int main(){
ll x,y,temp;
cin>>T;
while(T--){
scanf("%I64d%I64d%I64d",&x,&y,&temp);
printf("%I64d\n",lucas(x,y,temp));
}
}
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const ll label=1000000000000000LL;
template <typename T>
inline void _read(T& x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9'){if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
int T;
ll mod;
ll cnt;
ll n,m,n1,n2,c;
ll a[25];
ll A[205],M[205],prime[205],index[205];
ll F[50005];
ll mont(ll x,ll y,ll z){
ll temp=1;
for(x%=z;y;y>>=1,x=1ll*x*x%z){
if(y&1)temp=1ll*temp*x%z;
}
return temp;
}
void pre(){
ll MOD=mod,i,t;
for(i=2;i*i<=MOD;i++){
if(MOD%i==0){
t=0;
prime[++cnt]=i;
while(MOD%i==0){
MOD/=i;
t++;
}
M[cnt]=mont(i,t,label);
index[cnt]=t;
}
}
if(MOD>1){
M[++cnt]=MOD;
prime[cnt]=MOD;
index[cnt]=1;
}
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;y=0;return a;
}
else{
ll ans=exgcd(b,a%b,x,y);
ll t=x;
x=y;y=t-a/b*y;return ans;
}
}
ll _fac(ll n,ll t){
if(n<prime[t])return F[n];
c+=n/prime[t];
return 1ll*mont(F[M[t]-1],n/M[t],M[t])*F[n%M[t]]%M[t]*_fac(n/prime[t],t)%M[t];
}
ll C(ll n,ll m,ll t){
if(n<m)return 0;
if(m<0)return 0;
if(n==m)return 1;
ll i,p,temp,x,y,s;
F[0]=1;
for(i=1;i<M[t];i++){
if(i%prime[t])F[i]=F[i-1]*i%M[t];
else F[i]=F[i-1];
}
c=0;
p=_fac(n,t);
s=c;c=0;
temp=1ll*_fac(m,t)*_fac(n-m,t)%M[t];
exgcd(temp,M[t],x,y);
x=(x+M[t])%M[t];
p=1ll*p*x%M[t]*mont(prime[t],s-c,M[t])%M[t];
return p;
}
ll lucas(ll n,ll m){
if(n<m||m<0)return 0;
if(n==m)return 1;
//cout<<"lucas("<<n<<","<<m<<")"<<endl;
ll temp=0,x,y,i,t;
for(i=1;i<=cnt;i++){
//cout<<"i:"<<i<<" prime:"<<prime[i]<<" mod:"<<M[i]<<" index:"<<index[i]<<endl;
t=mod/M[i];
exgcd(t,M[i],x,y);x=(x+M[i])%M[i];
temp=(temp+1ll*x*C(n,m,i)%mod*t%mod)%mod;
}
return temp;
}
int main(){
ll i,j,k;
cin>>mod;
pre();
}
相關推薦
Lucas定理應用分析——大組合數取模
首先給出Lucas(盧卡斯)定理: 有非負整數A、B,和素數p,A、B寫成p進製為:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。 則組合數C(A,B)與C(a[n],b[n])×C(a[n-1],b[n-1])×...×C
大組合數取模-盧卡斯定理
求左邊的 為: 通過觀察你會發現當且僅當i = t , j = r ,能夠得到的係數,及。 所以,。得證。 -------------------------------------------------------------------------------------------
Lucas定理 大組合數取模
對於C(n, m) mod p。這裡的n,m,p(p為素數)都很大的情況。就不能再用C(n, m) = C(n - 1,m) + C(n - 1, m - 1)的公式遞推了。 這裡用到Lusac定理 For non-negative integers m and n and a prime p, the f
大組合數取模
考慮C(n,m)%P 情況一:n,m很大,P為素數 處理小範圍的階乘和階乘的逆元 用盧卡斯定理即可。盧卡斯定理: 情況二: 當P= p1∗p2∗p3∗...∗pn 求出[Cmn]分別在[p1,p2,p3,…,pn]模意義下的結果,記為 [m1,m
Lucas定理——大組合數取模
大組合數取模,求C[n][m]%p 公式:C[n][m]%p == C[n%p][m%p]*C[n/p][m/p]%p 注意,Lucas的要求是n,m<=10^5,如果n,m>=10^5,那麼要求p<=10^5 楊輝三角: f[0][
Lucas定理及組合數取模
引入 楊輝三角 std 數據 組合數取模 有關 ans main include 引入: 組合數C(m,n)表示在m個不同的元素中取出n個元素(不要求有序),產生的方案數。定義式:C(m,n)=m!/(n!*(m-n)!)(並不會使用LaTex QAQ)。 根據題目中對組合
組合數取模
ios AS names 局限性 代碼 lap div 沒有 AC 組合數取模問題為求$C_{n}^m % p$的值。根據$n$,$m$,$p$取值不同,方法不同。在此之前我們先看些前置技能: 同余定理:$a≡b(mod\ m)$性質:1.傳遞性:若$a≡b(mod\
組合數取模&&Lucas定理題集
pac 假設 次方 href ace 範圍 統一 lucas定理 != 題集鏈接: https://cn.vjudge.net/contest/231988 解題之前請先了解組合數取模和Lucas定理 A : FZU-2020 輸出組合數C(n, m) mod p (
Uva12034 (組合數取模)
傳遞 組合數取模 組合 並且 gen mod 總數 比賽結果 對組 題意:兩匹馬比賽有三種比賽結果,n匹馬比賽的所有可能結果總數 解法: 設答案是f[n],則假設第一名有i個人,有C(n,i)種可能,接下來還有f(n-i)種可能性,因此答案為 ΣC(n,i)f(n-i) 另
組合數取模1:盧卡斯定理
模板: #include<iostream> #include<algorithm> #include<cstdio> #define ll long long #define N 100005 using namespace std; int k,n,m
求組合數以及組合數取模
1、採用C(a, b) = n! / (m! * (n - m)!),適用範圍為n <= 20 typedef long long ll; const int maxn=20+5; ll a[maxn]; void init() { a[0]=1; for(int i=1; i&l
[學習筆記]組合數取模的幾種求法
一、引入 給定 n n n ,
[演算法 18_001] Lucas 定理與大組合數取餘
Lucas 定理 該定理是用來求當 (nm) ( n m
2018 Wannafly summer camp Day3-- Travel (思維 組合數取模)
題目大意: 魔方國有n座城市,編號為。城市之間通過n-1條無向道路連線,形成一個樹形結構。瀾瀾打算在魔方國進行m次旅遊,每次遊覽至少一座城市。為了方便,每次旅遊遊覽的城市必須是連通
組合數取模(逆元+快速冪)
組合大發好 一般我們用楊輝三角性質 楊輝三角上的每一個數字都等於它的左上方和右上方的和(除了邊界) 第n行,第m個就是,就是C(n, m) (從0開始) 電腦上我們就開一個數組儲存,像這樣 #include<cstdio> const int
各種逆元求法 組合數取模 comb (組合數 Lucas)
組合數取模(comb) 【問題描述】 計算C(m,n)mod 9901的值 【輸入格式】 從檔案comb.in中輸入資料。 輸入的第一行包含兩個整數,m和n 【輸出格式】 輸出到檔案comb.out中。 輸出一行,一個整數 【樣例輸入】 2
Codeforces 521C 組合數取模(乘法逆元)
【解題報告】 之前很少遇到組合數取模的問題(做題太少了),所以就GG了……組合數取模這一問題在演算法競賽中還是很常見的,必須紮實掌握。 回到這道題目來,你需要在n個數之間放k個加號,然後求出所有方案的和。 我們知道正向思維,即求出所有的方案,然後對每個
大數組合數取模(逆元+打表)
將階乘O(n)打表之後C(n,m)便可O(1)求出,除法取模用逆元解決 hdu5698瞬間移動 #include<bits/stdc++.h> using namespace std
求組合數取模(楊輝三角打表 & 求逆元(擴充套件歐幾里得、費馬小定理、尤拉定理、線性求法) & Lucas)
在acm競賽中,組合數取模的題目還是經常會見到的,所以這是有必要掌握的一個演算法。我本人就因為這個東西而被坑了很多次了= =之前的部落格也都扯過了,就不多說了,下面進入正題。 (1)楊輝三角求組合數 楊輝三角這個東西應該都不陌生,三角的兩邊始終為一,之後向
求解組合數取模---拓展歐幾里德和費馬小定理求解逆元
組合數:C(n, m) ; 組合數取模:C(n, m) % mod,mod是一個很大的數。1.公式:2.性質:(1)C(n,m)= C(n,n-m) 其中有C(n, 0) = 1;(2)C(n,m)=C(n-1,m-1)+C(n-1,m)。可以用作遞迴中的