0x36 组合计数

组合计算的性质:

C(n,m)= m! / (n!(m-n)!)

C(n,m)=C(m-n,m); C(n,m)=C(n,m-1)+C(n-1,m-1);

二项式定理:(a+b)^n=sigema(k=0~n) C(k,n)*a^k*b^(n-k)

lucas定理:C(n,m)≡C(n%p,m%p)*C(n/p,m/p)  (mod p)

catalan数: Cat(n)=C(n,2n)/n+1  Cat(n)=Cat(n-1)*(4n-2)/(n+1)

计算系数 通过二项式定理变形其实就是求C(n,k)*a^n*b^m,用下逆元

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=10007;

LL quick_pow(LL A,LL p)
{
    int ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=(ret*A)%mod;
        A=(A*A)%mod;p/=2;
    }
    return ret;
}
LL jiecheng(LL k)
{
    LL ret=1;
    for(int i=1;i<=k;i++)ret=(ret*i)%mod;
    return ret;
}
LL getniyuan(LL k)
{
    return quick_pow(k,mod-2);
}

int main()
{
    LL a,b,k,n,m;
    scanf("%lld%lld%lld%lld%lld",&a,&b,&k,&n,&m);
    LL ans=( (jiecheng(k)*getniyuan( jiecheng(n)*jiecheng(k-n)%mod )%mod) *
              (quick_pow(a,n)*quick_pow(b,m)%mod) )%mod;
    printf("%lld
",ans);
    return 0;
}
计算系数

Counting Swaps 神仙题,%lyd吧。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=1e9+9;

LL quick_pow(LL A,LL p)
{
    int ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=(ret*A)%mod;
        A=(A*A)%mod;p/=2;
    }
    return ret;
}
LL jiecheng(LL k)
{
    LL ret=1;
    for(int i=1;i<=k;i++)ret=(ret*i)%mod;
    return ret;
}
LL getniyuan(LL k)
{
    return quick_pow(k,mod-2);
}

int nxt[110000];
bool v[110000];
LL dfs(int x,int d)
{
    if(v[x]==true)return d;
    v[x]=true;
    dfs(nxt[x],d+1);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&nxt[i]);
        
        LL tot=0,ans=1,cnt;cnt=n;
        memset(v,false,sizeof(v));
        for(int i=1;i<=n;i++)
        {
            if(v[i]==false)
            {
                int L=dfs(i,0);
                if(L>1)
                {
                    tot++;
                    ans=(ans*quick_pow(L,L-2)%mod)*getniyuan(jiecheng(L-1))%mod;
                }
                else cnt--;
            }
        }
        if(tot==0)printf("1
");
        else
        {
            ans=ans*jiecheng(cnt-tot)%mod;
            printf("%lld
",ans);
        }
    }
    return 0;
}
Count Swaps

bzoj1951 由欧拉定理的推论,变成计算sigema(d|n)C(d,n)%(mod-1),这个用卢卡斯定理搞,但是mod不是质数,那么分解分别搞,然后得到4个同余方程,解出就是指数了,最后快速幂即可。

upd:感觉之前的阶乘模数有点假。。但是应该是对的

快速幂+crt并没有exgcd优秀的说。。。卡了一波常数才过的。。。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=999911659;
const LL md[]={2,3,4679,35617};

int quick_pow(int A,int p,int mod)
{
    int ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=(LL)ret*A%mod;
        A=(LL)A*A%mod;p/=2;
    }
    return ret;
}
LL fac[4][41000],fac_inv[4][41000];
void initC()
{
    for(int j=0;j<=3;j++)fac[j][0]=1,fac_inv[j][0]=1;
    for(int i=1;i<=md[3];i++)
        for(int j=0;j<=3;j++)
            fac[j][i]=fac[j][i-1]*i%md[j],fac_inv[j][i]=quick_pow(fac[j][i],md[j]-2,md[j]);
}
LL getC(LL n,LL m,LL p){return fac[p][n]*fac_inv[p][m]%md[p]*fac_inv[p][n-m]%md[p];}
LL lucas(LL n,LL m,LL p)
{
    LL ans=1;
    while(m>0)
    {
        LL a=n%md[p],b=m%md[p];
        if(a<b)return 0;
        ans=ans*getC(a,b,p)%md[p];
        n/=md[p],m/=md[p];
    }
    return ans;
}

LL a[4];
LL solve(LL n,LL k)
{
    for(int i=0;i<=3;i++)a[i]=lucas(n,k,i);
    LL M=mod-1,x=0;
    for(int i=0;i<=3;i++)//crt
        x=(x+a[i]*(M/md[i])*quick_pow(M/md[i],md[i]-2,md[i]))%(mod-1);
    return x;
}

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    initC();
    int n,G,p=0;
    scanf("%d%d",&n,&G);
    if(G==mod){printf("0
");return 0;}
    for(int i=1;i*i<=n;i++)
        if(n%i==0)
        {
            p=(p+solve(n,i))%(mod-1);
            if(i*i!=n)p=(p+solve(n,n/i))%(mod-1);
        }
    printf("%d
",quick_pow(G,p,mod));
    return 0;
}
bzoj1951(upd)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const LL mod=999911659;
const LL md[4]={2,3,4679,35617};

LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(a==0)
    {
        x=0;y=1;
        return b;
    }
    else
    {
        LL tx,ty;
        LL d=exgcd(b%a,a,tx,ty);
        x=ty-b/a*tx;
        y=tx;
        return d;
    }
}
LL quick_pow(LL A,LL p)
{
    LL ret=1;
    while(p!=0)
    {
        if(p%2==1)ret=(ret*A)%mod;
        A=(A*A)%mod;p/=2;
    }
    return ret;
}
LL inv(LL a,LL p)
{
    LL x,y;
    LL d=exgcd(a,p,x,y);
    return (x%p+p)%p;
}

//---------------------------------------------------

LL jc[110000];
LL lucas(LL N,LL M,LL p)
{
    LL ans=1;
    while(M>0)
    {
        LL a=N%p,b=M%p;
        if(a>b)return 0;
        
        ans=ans*jc[b]%p;
        ans=ans*inv(jc[a],p)%p;
        ans=ans*inv(jc[b-a],p)%p;
        
        N/=p;M/=p;
    }
    return ans;
}//lucas
LL g[110000];
void getzs(LL n)
{
    jc[0]=1;
    for(int i=1;i<=md[3];i++)jc[i]=(jc[i-1]*i)%(mod-1);
    
    for(int i=1;i*i<=n;i++)
    {
        if(n%i==0)
        {
            for(int j=0;j<=3;j++)
                g[j]=(g[j]+lucas(i,n,md[j]))%md[j];
            if(i!=n/i)
            {
                for(int j=0;j<=3;j++)
                    g[j]=(g[j]+lucas(n/i,n,md[j]))%md[j];
            }
        }
    }
}

//---------------------------------------------------

LL u1,v1,u2,v2;
void merge()
{
    LL A=u1,B=u2,K=v2-v1,x,y;
    LL d=exgcd(A,B,x,y);
    
    x=(x*(K/d)%(B/d)+(B/d))%(B/d);
    v1=u1*x+v1;
    u1=u1/d*u2;
}
LL solve()
{
    u1=md[0],v1=g[0];
    for(int i=1;i<=3;i++)
        u2=md[i], v2=g[i], merge();
    return v1;
}

int main()
{
    LL n,g;
    scanf("%lld%lld",&n,&g);g%=mod;
    if(g==0){printf("0
");return 0;}
    
    getzs(n);
    LL p=solve();
    printf("%lld
",quick_pow(g,p));
    return 0;
}
bzoj1951
原文地址:https://www.cnblogs.com/AKCqhzdy/p/9401743.html