[BZOJ1951][SDOI2010]古代猪文(Lucas定理+中国剩余定理)

题目:

我是超链接

题解:

题目就是让求 Gk|NC(N,k)%p G ∑ k | N C ( N , k ) % p
根据欧拉定理: aφ(n)1(%n) a φ ( n ) ≡ 1 ( % n ) ,我们知道指数只要到了φ(n),得出来的都是1了,那么柿子可以画成

Gk|NC(N,k)%φ(p)%p G ∑ k | N C ( N , k ) % φ ( p ) % p

然后因为模数是质数,φ(p)=p-1=999911658=2*3*4679*35617,这显然不是个质数了,模数不是质数怎么办啊?
可以用Lucas定理+中国剩余定理合并。诶这东西好像是扩展Lucas,但是要是真写的话会T,因为扩Lucas中的模数只是互质而不是质数。但这道题很好办啊都是质数
还有一个小细节就是当gcd(g,mod)!=1的时候不能使用欧拉定理,这个时候直接输出0就好咯

代码:

#include <cstdio>
using namespace std;
#define LL long long
const int mod=999911659;
const int phi=mod-1;
LL mul[5][35620],p[5],c[5],ans;
LL ksm(LL a,LL k,LL mod)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
LL gcd(LL a,LL b){if (!b) return a;else return gcd(b,a%b);}
void exgcd(int a,int b,LL &x,LL &y){if (!b) x=1,y=0;else exgcd(b,a%b,y,x),y-=a/b*x;}
LL C(LL n,LL m,int i)
{
    if (m>n) return 0;
    return mul[i][n]*ksm(mul[i][m]*mul[i][n-m]%p[i],p[i]-2,p[i])%p[i];
}
LL Lucas(LL n,LL m,int i)
{
    if (m>n) return 0;
    LL ans=1;
    for (;m;n/=p[i],m/=p[i]) ans=(ans*C(n%p[i],m%p[i],i))%p[i];
    return ans;
}
void init()
{
    p[1]=2; p[2]=3; p[3]=4679; p[4]=35617;
    for (int i=1;i<=4;i++)
    {
        mul[i][0]=1;
        for (int j=1;j<=35617;j++) mul[i][j]=j%p[i]*mul[i][j-1]%p[i];
    }
}
int main()
{
    init();LL n,g;
    scanf("%lld%lld",&n,&g);
    if (gcd(g,mod)!=1) {printf("0");return 0;}
    for (int i=1;i*i<=n;i++)
      if (n%i==0)
      {
        for (int j=1;j<=4;j++) c[j]=(c[j]+Lucas(n,i,j))%p[j];
        if (n/i!=i) for (int j=1;j<=4;j++) c[j]=(c[j]+Lucas(n,n/i,j))%p[j];
      }
    for (int i=1;i<=4;i++)
    {
        LL a=phi/p[i],b=p[i],x,y;
        exgcd(a,b,x,y);
        x=(x%b+b)%b;
        if (!x) x+=b;
        ans+=c[i]*a*x;
    }
    ans=ksm(g,ans,mod);
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值