bzoj 1951

这道题告诉了我们一个很重要的道理:看到题,先想明白再动手!

题意:求\large G^{\sum_{m|n}C_{n}^{m}}对999911659取模的值

首先,由于n的数据范围不是很大(至少\large \sqrt{n}不是很大),所以可以O(\large \sqrt_{n})枚举所有约数分别求组合数

但是有个问题:根据费马小定理,\large \begin{matrix} a^{x} & mod &p & \equiv & a^{x mod \psi (p)}(mod p) \end{matrix}

所以组合数应当对p-1取模!

可是p-1并不是一个质数啊

所以我们要将p-1质因子分解,发现可以分解成四个质数之积,那么我们用四次卢卡斯定理分别计算出四个结果再用中国剩余定理合并即可。

但我真正想说的是,如果做过礼物的话,很容易误以为这题要用拓展卢卡斯定理,然后写到死...

所以千万不要像我一样...

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define mode 999911659
#define mo 999911658
#define ll long long
using namespace std;
ll inv[40000][5];
ll mul[40000][5];
ll mod[5]={0,2,3,4679,35617};
ll a[5];
ll n,g;
ll pow_mul(ll x,ll y)
{
    ll ans=1;
    while(y)
    {
        if(y%2)
        {
            ans*=x;
            ans%=mode;
        }   
        x*=x;
        x%=mode;
        y/=2;
    }
    return ans;
}
void init()
{
    for(int i=1;i<=4;i++)
    {
        inv[0][i]=inv[1][i]=mul[0][i]=mul[1][i]=1;
        for(int j=2;j<mod[i];j++)
        {
            inv[j][i]=(mod[i]-mod[i]/j)*inv[mod[i]%j][i]%mod[i];
        }
        for(int j=2;j<mod[i];j++)
        {
            inv[j][i]=inv[j-1][i]*inv[j][i]%mod[i];
            mul[j][i]=mul[j-1][i]*j%mod[i];
        }
    }
}
ll C(ll x,ll y,ll num)
{
    if(x<y)
    {
        return 0;
    }else if(x==y)
    {
        return 1;
    }else if(x<mod[num])
    {
        return mul[x][num]*inv[y][num]%mod[num]*inv[x-y][num]%mod[num];
    }else
    {
        return C(x/mod[num],y/mod[num],num)*C(x%mod[num],y%mod[num],num)%mod[num];
    }
}
void ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    ex_gcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*x;
}
ll china()
{
    ll M=mo;
    ll ans=0;
    for(int i=1;i<=4;i++)
    {
        ll M0=M/mod[i];
        ll x,y;
        ex_gcd(M0,mod[i],x,y);
        x=(x%mod[i]+mod[i])%mod[i];
        ans+=x*M0%mo*a[i]%mo;
    }
    return ans;
}
ll solve(ll x,ll y)
{
    for(int i=1;i<=4;i++)
    {
        a[i]=C(x,y,i);
    }
    return china();
}
int main()
{
    scanf("%lld%lld",&n,&g);
    init();
    ll s=0;
    for(int i=1;i*i<=n;i++)
    {
        if(n%i==0)
        {
            s+=solve(n,i);
            s%=mo;
            if(n/i!=i)
            {
                s+=solve(n,n/i);
            }
        }
         
    }
    printf("%lld\n",pow_mul(g,s));
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值