P2480 [SDOI2010]古代猪文 (欧拉函数,Lucas定理,CRT,组合数学)

P2480 [SDOI2010]古代猪文

Lucas定理:(组合数学取模运算)

C n m ≡ C n   m o d   p m   m o d   p ∗ C n / p m / p   m o d   p C_n^m \equiv C_{n\ mod\ p}^{m\ mod\ p}*C_{n/p}^{m/p}\bmod p CnmCn mod pm mod pCn/pm/pmodp

分析:
  • 数论大杂烩:欧拉函数, L u c a s Lucas Lucas定理, C R T CRT CRT

  • 由欧拉定理推论,转换一下,题目让求的就是:
    q ∑ d ∣ n C n d ≡ q ∑ d ∣ n C n d     m o d     999911658 (   m o d   999911659 ) q^{\sum_{d|n}C_{n}^d}\equiv q^{\sum_{d|n}C_{n}^d\ \bmod\ 999911658}(\bmod 999911659) qdnCndqdnCnd mod 999911658(mod999911659)

  • 问题就转换成了求: ∑ d ∣ n C n d   m o d   999911658 \sum_{d|n}C_{n}^d\bmod 999911658 dnCndmod999911658

    质因数分解, 999911658 = 2 ∗ 3 ∗ 4679 ∗ 35617 999911658=2*3*4679*35617 999911658=23467935617 ,发现 4 4 4 个质因数指数都是 1 1 1

  • 然后枚举 n n n 的约数,运用 L u c a s Lucas Lucas 定理求解组合数,分别计算出 ∑ d ∣ n C n d \sum_{d|n}C_{n}^d dnCnd 4 4 4 个质因数取模的结果,记为 a 1 , a 2 , a 3 , a 4 a_1,a_2,a_3,a_4 a1,a2,a3,a4

    之后再根据 C R T CRT CRT 推回实际的 ∑ d ∣ n C n d   m o d   999911658 \sum_{d|n}C_{n}^d\bmod 999911658 dnCndmod999911658

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e5+5, mo=999911658; 
// 注意mo这里先提前-1,最后算答案的时候还要加回来

int b[5]={0,2,3,4679,35617}, a[5];
// 999911658 质因数分解成 2,3,4679,35617
int fc[N];
void init(int n)
{
    fc[0]=1;
    for(int i=1;i<=n;i++) fc[i]=fc[i-1]*i%n;  
}
int ksm(int a,int b,int p)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%p;
        b>>=1; a=a*a%p;
    }
    return ans;
}
int C(int n,int m,int p) // 组合数
{
    if(n<m) return 0;
    return fc[n]*ksm(fc[m],p-2,p)%p*ksm(fc[n-m],p-2,p)%p;
} 
int lucas(int n,int m,int p)
{
    if(m>n) return 0;
    if(m==0) return 1;
    return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;
}
int crt(int p) // 中国剩余定理,合并回去,返回答案
{
    int ans=0;
    for(int i=1;i<=4;i++) 
    {
        ans=(ans+a[i]*(p/b[i])%p*ksm(mo/b[i],b[i]-2,b[i]))%p;
    }
    return ans;
}
signed main()
{
    ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int n,q;
    cin>>n>>q;
    if(q%(mo+1)==0) 
    {
        cout<<"0"<<endl; return 0;
    }
    for(int i=1;i<=4;i++)
    {
        init(b[i]);
        for(int j=1;j*j<=n;j++)
        {
            if(n%j==0)
            {
                a[i]=(a[i]+lucas(n,j,b[i]))%b[i];
                int t=n/j;
                if(t!=j) a[i]=(a[i]+lucas(n,t,b[i]))%b[i];
            }
        }
    }
    int ans=crt(mo);
    cout<<ksm(q,ans,mo+1)<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yezzz.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值