bzoj1951: [Sdoi2010]古代猪文

数论综合。费马小定理,lucas定理,中国剩余定理,exgcd,快速幂,乘法逆元。

首先要计算出n的每个约数,简单的sqrt(n)枚举即可。

然后计算C(i,m)(m个中挑i个的组合数,ps:因为网上正反俩种都有,所以标注一下。。)

设s=sum(C(i,m))

题目要求g^(s)%mod,

由费马小定理得 g^(s)=g^(s%(mod-1))。所以这里需要特判一下g是否等于mod。

可是组合数计算因为用到除法,所以模数必须为质数。999911659-1=2×3×4679×35617。

这四个数设为d[i],对这四个数分别进行计算。

由因为组合数很大,直接计算会超时。

需要用到lucas定理。 lucas(n,m)=lucas(n/mod,m/mod)*C(n%mod,m%mod)。这里mod不是999911659,而是那4个数。

所以就得到4个答案m[i]。

组合数的计算可以根据费马小定理转换成快速幂计算。

然后再用中国剩余定理合并。

这时得到的答案可以写成如下形式:

s%d[0]=m[0],s%d[1]=m[1];

改写为 s%a1=b1,s%a2=b2 (1)

则有 a1*x+b1=a2*y+b2 (x,y分别为俩个未知数,和上面的任何数没有任何关系)

设a=a1,b=a2,c=b2-b1

则上式变为 ax+by=c (正负号并没有关系,因为不会用到y)

计算t=extended_gcd(a,b,x,y)

如果c%t !=0 的话,说明此方程是无解的。在本题中t=1。(一下都基于本题t=1的情况)

然后我们计算出的是 ax+by=t。 所以我们将每一项乘以c。

x=(x*c%b+b)%b。

当然这样的x有无数组为 x1=x+k*b。

带入(1)式有  s=a1(x+k*b)+b1。

s=a1*b*k+a1*x+b1.

所以有 s%(a1*b)=a1*x+b1。

所以 b1=b1+a1*x,a1=a1*b。

在本题中这样操作完以后a1>b1,实际上即使t!=1,b1也会小于1。

最后合并完返回b1即可。

EN TARO Tassadar

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long 
using namespace std;
const int mod = 999911659;
const int d[]={2,3,4679,35617};
int fac[4][40000],m[4];
int n,g;

int power(LL a,int e,int mod) {
    LL res=1;
    while(e) {
        if(e&1) res=res*a%mod;
        a=a*a%mod;
        e>>=1;
    }
    return res;
}

int C(int n,int m,int x) {
    if(n<m) return 0;    
    return fac[x][n]*power(fac[x][m]*fac[x][n-m]%d[x],d[x]-2,d[x])%d[x];
}

int lucas(int n,int m,int x) {
    if(m==0) return 1;
    return lucas(n/d[x],m/d[x],x)*C(n%d[x],m%d[x],x)%d[x];    
}

int exgcd(int a,int b,int &x,int &y) {
    if(b==0) {
        x=1; y=0; return a;
    }
    else {
        int t=exgcd(b,a%b,y,x);
        y-=(a/b)*x;
        return t;
    }
}

int solve() {
    int a1,b1,a2,b2,a,b,c,x,y;    
    a1=d[0]; b1=m[0];
    for(int i=1;i<4;i++) {
        a2=d[i];b2=m[i];
        a=a1,b=a2,c=b2-b1;
        exgcd(a,b,x,y);
        x=(c*x%b+b)%b;//if(c%t) printf("Test\n");
        b1=b1+a1*x;
        a1=a1*b;
    }
    return b1;
}

int main() {
    for(int i=0;i<4;i++) {
        fac[i][0]=1;
        for(int j=1;j<=40000;j++) fac[i][j]=fac[i][j-1]*j%d[i];
    }
    scanf("%d%d",&n,&g);
    if(g==mod) printf("0\n");
    else {
        g=g%mod;    
        int n2=(int)sqrt(n+0.5);
        for(int i=1,j;i<=n2;i++) 
        if(n%i==0) {
            j=n/i;
            for(int x=0;x<4;x++) {
                m[x]=(m[x]+lucas(n,i,x))%d[x];
                if(i!=j) m[x]=(m[x]+lucas(n,j,x))%d[x];    
            }
        }
        printf("%d\n",power(g,solve(),mod));
    }
    return 0;    
}
posted @ 2016-07-05 22:47  invoid  阅读(236)  评论(0编辑  收藏  举报