组合数学
一、组合数取模
1、n,m小
递推式直接求
void C_init(int n){
for(int i=0;i<=n;++i){
C[i][0]=1;
for(int j=1;j<=i;++j)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
}
2、n,m大,p小且为质数:Lucas
提前处理阶乘和逆元
ll power(ll a,ll k){ ll m=1; while(k){ if(k&1) m=m*a%mod; a=a*a%mod;k>>=1; }return m;//函数一定要有返回值 } ll C(int n,int m){ if(n<m) return 0; return f[n]*inv[m]%mod*inv[n-m]%mod; } ll Lucas(int n, int m) { if (m == 0) return 1; return (C(n % mod, m % mod) * Lucas(n / mod, m / mod)) % mod; }
3、1<=M<=N<=10^6,1<=P<=10^5,P可能为合数
暴力分解,然后快速幂
int prime[MAX],num; bool is[MAX]; void isprime(){ for(int i=2;i<=MAX;++i){ if(!is[i]) prime[++num]=i; for(int j=1;j<=num&&i*prime[j]<=MAX;++j){ is[i*prime[j]]=1; if(i%prime[j]==0) break; } } } ll power(ll a,ll b){ ll m=1; while(b){ if(b&1) m=m*a%mod; b>>=1;a=a*a%mod; }return m; } ll work(ll n,ll p){ ll ans=0; while(n) n/=p,ans+=n; return ans; } ll C(ll n,ll m,ll p){ ll ans=1; for(int i=1;i<=num&&prime[i]<=n;++i){ ll x=work(n,prime[i]);ll y=work(n-m,prime[i]);ll z=work(m,prime[i]); x-=y+z;ans=ans*power(prime[i],x)%p; }return ans; }
4、n,m,p大,p为合数
分解质因子次数>1:扩展Lucas

#include<bits/stdc++.h> using namespace std; #define ll long long const int MAX=100010; ll n,m,mod; int p[MAX],cnt,co[MAX]; inline ll read(){ ll x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } ll power(ll a,ll b,int mod){// ll m=1; while(b){ if(b&1) m=m*a%mod; b>>=1;a=a*a%mod; }return m; } ll cal(ll n,ll pi,ll pk){//计算阶乘部分 (质因子已经提前处理这里不予考虑) if(!n) return 1; ll re=1; for(int i=2;i<pk;++i)//每个循环节 if(i%pi) re=(re*i)%pk; re=power(re,n/pk,pk); for(int i=2;i<=n%pk;++i) if(i%pi) re=(re*i)%pk; return re*cal(n/pi,pi,pk)%pk; } void zys(ll n){// for(int i=2;i<=n;++i) while(n!=i){ if(n%i==0){ if(p[cnt]!=i)p[++cnt]=i; co[cnt]++;n/=i; } else break; } if(p[cnt]!=n)p[++cnt]=n;co[cnt]++; } inline void exgcd(ll a,ll b,ll &x,ll &y)//long long变量要赋全 // { if(!b){x=1;y=0;return;} exgcd(b,a%b,x,y); int t=x;x=y;y=t-a/b*y; } ll inv(ll n,ll pk){//逆元 ll x,y;exgcd(n,pk,x,y); x=(x%pk+pk)%pk; return x; } ll crt(ll b,int p){// return b*inv(mod/p,p)%mod*(mod/p)%mod; } ll work(ll n,ll p){// ll ans=0; while(n) n/=p,ans+=n; return ans; } ll C(ll n,ll m,ll pi,ll pk){// ll up=cal(n,pi,pk),d1=cal(m,pi,pk),d2=cal(n-m,pi,pk); ll x=work(n,pi);ll y=work(n-m,pi);ll z=work(m,pi); return up*inv(d1,pk)%pk*inv(d2,pk)%pk*power(pi,x-y-z,pk)%pk; } int main(){ //freopen("1.txt","r",stdin); n=read();m=read();mod=read(); ll res=0; for(int j=1;j<=cnt;++j) res=(res+crt(C(n,m,p[j],pow(p[j],co[j])),pow(p[j],co[j])))%mod; }
次数=1:

#include<bits/stdc++.h> using namespace std; #define ll long long const int MAX=100010; ll a[MAX],n,m,mod,num,f[MAX],inv[MAX]; int p[MAX],cnt; inline ll read(){ ll x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } void zys(ll n){ for(int i=2;i<=n;++i) while(n!=i){ if(n%i==0){p[++cnt]=i;n/=i;} else break; } p[++cnt]=n; } ll power(ll a,ll b,int mod){ ll m=1; while(b){ if(b&1) m=m*a%mod; b>>=1;a=a*a%mod; }return m; } ll C(ll n,ll m,int mod){ if(n<m) return 0; return f[n]*inv[m]%mod*inv[n-m]%mod; } inline void exgcd(ll a,ll b,ll &x,ll &y)//long long变量要赋全 { if(!b){x=1;y=0;return;} exgcd(b,a%b,x,y); int t=x;x=y;y=t-a/b*y; } ll crt(int k,ll a[],int r[]){ ll gf=1,ans=0; for(int i=1;i<=k;++i) gf*=r[i];//计算所有模数的积 //cout<<gf<<endl; for(int i=1;i<=k;++i){// 对于第i个方程 ll m=gf/r[i],b,y; exgcd(m,r[i],b,y);//计算mi的逆元 ans=(ans+a[i]*m*b%gf)%gf;//不要对r[i]取模 }return (ans%gf+gf)%gf; } ll Lucas(int n, int m,int mod) { if (m == 0) return 1;//cout<<n<<' '<<m<<" "<<C(n % mod, m % mod,mod)<<endl; return (C(n % mod, m % mod,mod) * Lucas(n / mod, m / mod,mod)) % mod; } int main(){ //freopen("1.txt","r",stdin); n=read();m=read();mod=read(); zys(mod);f[0]=1; for(int j=1;j<=cnt;++j){ //cout<<p[j]<<endl;// for(int k=1;k<p[j];++k) f[k]=f[k-1]*k%p[j]; inv[p[j]-1]=power(f[p[j]-1],p[j]-2,p[j]);//cout<<inv[p[j]]<<endl; for(int k=p[j]-2;k>=0;--k) inv[k]=inv[k+1]*(k+1)%p[j]/*,cout<<inv[k]<<' '*/; a[j]=Lucas(n,w[i],p[j]);//cout<<n<<" "<<w[i]<<" "<<p[j]<<endl; }cout<<crt(cnt,a,p)%mod; }
二、中国剩余定理(CRT)
适用:求解一元线性同余方程(其中n1,n2,……,nk两两互质)
代码:
ll crt(int k,ll a[],ll r[]){ ll gf=1,ans=0; for(int i=1;i<=k;++i) gf*=r[i];//计算所有模数的积 for(int i=1;i<=k;++i){// 对于第i个方程 ll m=gf/r[i],b,y; exgcd(m,r[i],b,y);//计算mi的逆元 ans=(ans+a[i]*m*b%gf)%gf;//不要对r[i]取模 }return (ans%gf+gf)%gf; }//a为余数,b为除数
扩展中国剩余定理
适用:ni不互质,判断有无解并求最小整数解
代码:

#include<bits/stdc++.h> using namespace std; #define ll long long ll n,a[100005],b[100005]; inline ll read(){ ll x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } ll gcd(ll n,ll m){ if(n%m==0) return m; return gcd(m,n%m); } inline void exgcd(ll a,ll b,ll &x,ll &y)//long long变量要赋全 { if(!b){x=1;y=0;return;} exgcd(b,a%b,x,y); int t=x;x=y;y=t-a/b*y; } ll mul(ll a,ll b,ll c) { ll m=0;a%=c; while(b){ if(b&1) m=(m+a)%c; b>>=1;a=(a+a)%c; }return m; } inline ll excrt(){ ll x,y,M=b[1],ans=a[1]; for(int i=2;i<=n;++i){ ll ai=M,bi=b[i],c=(a[i]-ans%bi+bi)%bi; exgcd(ai,bi,x,y);ll d=gcd(ai,bi),bg=bi/d; if(c%d) return -1; x=mul(x,c/d,bg);//chenghuanjia,防止溢ll ans+=x*M;M*=bg; ans=(ans%M+M)%M; }return (ans%M+M)%M; } int main(){ while(cin>>n){ for(int i=1;i<=n;++i) b[i]=read(),a[i]=read(); cout<<excrt()<<endl; } }
三、 二项式定理
证明1——数学归纳法
当n=1是,( a + b )=C(1,0)(a0+b1)+C(1,1)(a1+b0)=a+b
使一项中k=kk-1,变为
两项加起得再依据
得
证毕
证明2——组合数学
(a+b)(a+b)…(a+b)一共n个(a+b)中,考虑挑选i个a与剩下的n-i个b相乘,有C(n,i)种方案
可扩展为多项式
四、一些公式
m不变,n变,求和
n+m不变,求和
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)