【BZOJ】【中国剩余定理】1951: [Sdoi2010]古代猪文

题意


Gi|n(ni)(mod999911659) G ∑ i | n ( n i ) ( mod 999911659 )

题解

由费马小定理

ap11(modp) a p − 1 ≡ 1 ( mod p )


Gi|n(ni)(modtt)=Gi|n(ni)(modtt1)(modtt) G ∑ i | n ( n i ) ( mod t t ) = G ∑ i | n ( n i ) ( mod t t − 1 ) ( mod t t )

但是,由于 n n 很大,所以无法直接求组合数,考虑将tt1质因数分解后用 Lucas L u c a s 算出每个答案对每个质因数取模的余数,再用中国剩余定理合并。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int maxn=16,maxs=35617,tt=999911659;
LL n,m,G,M[maxn],a[maxn],pr[maxs+6],f[maxn][maxs+6],inv[maxn][maxs+6];
bool vis[maxs+6];
void make_pr(){
    for(int i=2;i<=maxs;i++){
        if(!vis[i])pr[++pr[0]]=i;
        for(int j=1;j<=pr[0]&&pr[j]<=maxs/i;j++){
            vis[pr[j]*i]=1;
            if(!(i%(pr[j])))break;
        }
    }
}
LL C(LL x,LL y,int t){if(x<y)return 0;return f[t][x]*inv[t][y]%M[t]*inv[t][x-y]%M[t];}
LL lucas(LL x,LL y,int t){
    if(!y)return 1;
    return C(x%M[t],y%M[t],t)*lucas(x/M[t],y/M[t],t)%M[t];
}
LL power(LL x,LL y){
    LL w=x,sum=1;if(!(x%tt))sum=0;
    while(y){
        if(y&1)(sum*=w)%=tt;
        y>>=1;(w*=w)%=tt;
    }return sum;
}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
LL exgcd(LL a,LL b,LL&x,LL&y){if(!b)return x=1,y=0,a;LL r=exgcd(b,a%b,y,x);return y-=a/b*x,r;}
LL lcm(LL a,LL b){return a/gcd(a,b)*b;}
LL solve(LL a,LL b,LL c){
    b%=c;if(b<0)b+=c;;LL x,y,r=exgcd(a,c,x,y);if(b%r)return -1;
    c/=r;return (x*(b/r)%c+c)%c;
}
LL CRT(int len,LL*a,LL*m){
    LL A=a[1],M=m[1];
    for(int i=2;i<=len;i++){
        LL x=solve(m[i],A-a[i],M);if(x==-1)return -1;
        M=lcm(M,m[i]);A=m[i]*x%M+a[i];if(A>=M)A-=M;
    }return A;
}
int main(){
    freopen("pig.in","r",stdin);freopen("pig.out","w",stdout);
    scanf("%lld%lld",&n,&G);make_pr();LL X=tt-1;
    for(int i=1;i<=pr[0]&&pr[i]<=sqrt(tt-1);i++) if(!(X%pr[i])){
        M[++m]=pr[i];
        while(X>1&&!(X%pr[i]))X/=pr[i];
    }
    if(X>1)M[++m]=X;
    for(int t=1;t<=m;t++){
        f[t][0]=inv[t][1]=inv[t][0]=1;
        for(int i=1;i<=maxs;i++)f[t][i]=f[t][i-1]*i%M[t];
        for(int i=2;i<=maxs;i++)inv[t][i]=(M[t]-(LL)M[t]/i*inv[t][M[t]%i]%M[t])%M[t];
        for(int i=2;i<=maxs;i++)(inv[t][i]*=inv[t][i-1])%=M[t];
    }
    for(int i=1;i<=sqrt(n);i++) if(!(n%i)){
        for(int j=1;j<=m;j++) (a[j]+=lucas(n,i,j))%=M[j];
        if(i*i<n)for(int j=1;j<=m;j++)(a[j]+=lucas(n,n/i,j))%=M[j];
    }
    printf("%lld",power(G,CRT(m,a,M)));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值