LightOJ1318 Strange Game(组合数求模)

题目链接:http://lightoj.com/volume_showproblem.php?problem=1318

题意:长度为L的单词由大小为K的字母表中的字母组成,这些单词组成的集合我们不妨称作S。每次从S中选出2个单词si,sj。若si和sj恰有M个位置上的字母不同,称(si,sj)为一个合法的二元组合。可知,合法的二元组合有很多。输出合法二元组合的个数模N。

思路:题目即是求K^L*C(L,M)*(K-1)^M/2%N的值。因为K^L*(K-1)^M必行能被2整除。因此,剩下的就是计算C(L,M)%N的值,Lucas定理即可解决。

#include <iostream>

#include <cstdio>

#define i64 long long

using namespace std;



int c,num=0;



i64 N,K,L,M;



i64 POW(i64 a,i64 b,i64 mod)

{

    i64 ans=1;

    while(b)

    {

        if(b&1) ans=ans*a%mod;

        a=a*a%mod;

        b>>=1;

    }

    return ans;

}



i64 POW(i64 a,i64 b)

{

    i64 ans=1;

    while(b)

    {

        if(b&1) ans=ans*a;

        a=a*a;

        b>>=1;

    }

    return ans;

}





i64 exGcd(i64 a,i64 b,i64 &x,i64 &y)

{

    i64 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(i64 a[],i64 m[],i64 k)

{

    i64 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;

}







i64 reverse(i64 a,i64 b)

{

    i64 x,y;

    exGcd(a,b,x,y);

    return (x%b+b)%b;

}



i64 C(i64 n,i64 m,i64 mod)

{

    if(m>n) return 0;

    i64 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;

}



i64 C1(i64 n,i64 m,i64 mod)

{

    if(m==0) return 1;

    return C(n%mod,m%mod,mod)*C1(n/mod,m/mod,mod)%mod;

}



i64 cal(i64 n,i64 p,i64 t)

{

    if(!n) return 1;

    i64 x=POW(p,t),i,y=n/x,temp=1;

    for(i=1;i<=x;i++) if(i%p) temp=temp*i%x;

    i64 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;

}



i64 C2(i64 n,i64 m,i64 p,i64 t)

{

    i64 x=POW(p,t);

    i64 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;

    i64 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

i64 Lucas(i64 n,i64 m,i64 mod)

{

    i64 i,t,cnt=0;

    i64 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];

}







i64 get()

{

    if(!M) return POW(K,L,N)+1;

    i64 ans=1;

    if(K&1) ans=ans*POW(K,L,N)*(K-1)/2%N*POW(K-1,M-1,N)%N;

    else ans=ans*POW(K-1,M,N)*K/2%N*POW(K,L-1,N)%N;

    ans=ans*Lucas(L,M,N)%N;

    return ans+1;

}



int main()

{

    for(scanf("%d",&c);c--;)

    {

        scanf("%lld%lld%lld%lld",&N,&K,&L,&M);

        printf("Case %d: %lld\n",++num,get());

    }

    return 0;

}

  

 

你可能感兴趣的:(game)