Lucas定理的三类问题

http://www.cnblogs.com/jianglangcaijin/p/3446839.html


Lucas定理用来求解大组合数取余 C(n,m)%mod,具体证明啊,我就不证了,只是来当黑盒,==,原谅我的弱。。


第一类 C(n,m)%mod,mod 是个质数,这样的话,直接套模板,HDU3037。。

ll getFactor(ll p)//求因子
{
    fac[0] = 1;
    for(int i = 1;i<=p;i++)
    {
        fac[i] = (fac[i - 1]*i)%p;
    }
}

ll Lucas(ll n,ll m,ll p)//大组合数取余
{
    ll ret = 1;
    while(n&&m)
    {
        ll a = n%p, b = m%p;
        if(a<b) return 0;
        ret = (ret*fac[a]*modpow(fac[b]*fac[a - b]%p , p - 2, p)) %p;
        n/=p;
        m/=p;
    }
    return ret;
}

第二类 C(n,m)%mod,mod 是合数,这样的话,把mod变成质数的乘积,然后对每个质数求lucas,最后用中国剩余定理,求解一元用余方程,HDU5446。。

ll fac[MAX];//因子
ll p[109];
ll lucas[109];
ll n,m,k;
ll modpow(ll a,ll b,ll mod)//a^b%mod
{
    ll ret = 1;
    while(b)
    {
        if(b&1) ret = (ret*a)%mod;
        a = (a*a)% mod;
        b>>=1;
    }
    return ret;
}
ll modmul(ll a,ll b,ll mod)//a*b%mod;
{
    ll ret = 0;
    while(b)
    {
        if(b&1) ret = (ret + a)%mod;
        a = (a + a)% mod;
        b>>=1;
    }
    return ret;
}
ll getFactor(ll p)//求因子
{
    fac[0] = 1;
    for(int i = 1;i<=p;i++)
    {
        fac[i] = (fac[i - 1]*i)%p;
    }
}
ll Lucas(ll n,ll m,ll p)//大组合数取余
{
    ll ret = 1;
    while(n&&m)
    {
        ll a = n%p, b = m%p;
        if(a<b) return 0;
        ret = (ret*fac[a]*modpow(fac[b]*fac[a - b]%p , p - 2, p)) %p;
        n/=p;
        m/=p;
    }
    return ret;
}
ll exgcd(ll a,ll b,ll &x,ll &y)//拓展欧几里得
{
    if(!b)
    {
        x = 1 , y = 0;
        return a;
    }
    int ans = exgcd(b , a%b, y , x);
    y-=a/b*x;
    return ans;
}
ll CRT(ll *a,ll *m,int len)//中国剩余定理
{
    ll d,x,y,ret = 0;
    ll M = 1;
    for(int i = 0;i < len;i++) M*=m[i];
    for(int i = 0;i < len;i++)
    {
        ll w = M/m[i];
        d = exgcd(m[i],w,x,y);
        ret = (ret + modmul(modmul(y, w, M), a[i], M) ) % M;
    }
    return (ret + M) % M;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("an.txt","r", stdin);
#endif
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%d",&n,&m,&k);
        for(int i = 0; i<k; i++)
        {
            scanf("%I64d",&p[i]);
            getFactor(p[i]);
            lucas[i] = Lucas(n,m,p[i]);
        }
        ll ans = CRT(lucas,p,k);
        printf("%I64d\n",ans);
    }
    return 0;
}

第三类, C(n,m)%mod^t

ll POW(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}


ll POW(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}




ll exGcd(ll a,ll b,ll &x,ll &y)
{
    ll t,d;
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    d=exGcd(b,a%b,x,y);
    t=x;
    x=y;
    y=t-a/b*y;
    return d;
}


bool modular(ll a[],ll m[],ll k)
{
    ll d,t,c,x,y,i;
    for(i=2;i<=k;i++)
    {
        d=exGcd(m[1],m[i],x,y);
        c=a[i]-a[1];
        if(c%d) return false;
        t=m[i]/d;
        x=(c/d*x%t+t)%t;
        a[1]=m[1]*x+a[1];
        m[1]=m[1]*m[i]/d;
    }
    return true;
}






ll reverse(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=reverse(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=POW(p,t),i,y=n/x,temp=1;
    for(i=1;i<=x;i++) if(i%p) temp=temp*i%x;
    ll ans=POW(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=POW(p,t);
    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=POW(p,ap,x);
    a=cal(n,p,t);
    b=cal(m,p,t);
    c=cal(n-m,p,t);
    ans=ans*a%x*reverse(b,x)%x*reverse(c,x)%x;
    return ans;
}


//计算C(n,m)%mod
ll Lucas(ll n,ll m,ll mod)
{
    ll i,t,cnt=0;
    ll A[205],M[205];
    for(i=2;i*i<=mod;i++) if(mod%i==0)
    {
        t=0;
        while(mod%i==0)
        {
            t++;
            mod/=i;
        }
        M[++cnt]=POW(i,t);
        if(t==1) A[cnt]=C1(n,m,i);
        else A[cnt]=C2(n,m,i,t);
    }
    if(mod>1)
    {
        M[++cnt]=mod;
        A[cnt]=C1(n,m,mod);
    }
    modular(A,M,cnt);
    return A[1];
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值