组合数学

 一、组合数取模

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;
}
View Code
复制代码

 次数=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;
}
View Code
复制代码

二、中国剩余定理(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;
    }
}
View Code
复制代码

三、 二项式定理

 

证明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不变,求和

posted @   yisiwunian  阅读(306)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示