2019年西安邀请赛B:Product 【杜教筛+降幂】

题目:

2019年西安邀请赛B:Product

题意:

给定 N,M,P,求下面式子的值最后模P:

\prod_{i=1}^{n}\prod_{j=1}^{n}\prod_{k=1}^{n}m^{gcd(i,j)[k|gcd(i,j)]}

分析:

m 的值恒定,先考虑求m最后的幂,即:

=\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=1}^{n}gcd(i,j)[k|gcd(i,j)]

=\sum_{g=1}^{n}g*d(g)\sum_{i=1}^{\left \lfloor \frac{n}{g} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{g} \right \rfloor}[gcd(i,j)==1]

=\sum_{g=1}^{n}g*d(g)*F(\frac{n}{g})

上式中 d(x) 表示 x 的约数的个数;F 函数的值是一段一段的,每一段都相等,如果知道了 g*d(g) 的前缀和就可以分块加速枚举 g; 设其前缀和 G(n) 为:

=\sum_{i=1}^{n}i*d(i)

=\sum_{i=1}^{n}\sum_{d|i}i

=\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}i

=\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}i

完美!!!这个求和式是可以分块快速计算的;就剩下 F 函数的值,如果用反演求,但 n 比较大,还是要用杜教筛计算莫比乌斯函数的前缀和,实际上 F(n) 也等价于:

=(\sum_{i=1}^{n}\varphi (i))*2-1

欧拉函数前缀和是可以杜教筛快速求的;令 h(n) = (I * phi)(n),(I 为恒等函数,即:I(x) = 1)写成狄利克雷卷积展开式有 h(n):

=\sum_{d|n}\varphi (d)I(\frac{n}{d})

=\sum_{d|n}\varphi (d)

 =n

最后一步是欧拉函数的性质,那么就有:

\sum_{i=1}^{n}h(i)=\sum_{i=1}^{n}\sum_{d|i}\varphi (d)=\sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\varphi (i)

提出左边式子的第一项,得到:

\sum_{i=1}^{n}h(i)=\sum_{d=2}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\varphi (i)+\sum_{i=1}^{n}\varphi (i)

设 S(n) 为欧拉函数的前缀和,有:

S(n)=\sum_{i=1}^{n}h(i)-\sum_{d=2}^{n}S(\left \lfloor \frac{n}{d} \right \rfloor)

上面已经推出 h(x) = x,那么递归求S(n)即可;

降幂直接对(p-1)取模就好了;为什么呢?根据广义欧拉降幂公式可知如果 gcd(m,p) = 1时,直接取余是成立的;如果不等于1呢?只可能为:m 是 p 的倍数,此时不管幂是多少答案都是0,当然幂也不能为0,依然成立

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 1e6+56;
const int maxm = 7e5+75;
LL n,m,p,phi[maxn],prime[maxm],d[maxn];
unordered_map<LL,LL> mp;
LL qpow(LL a,LL x,LL mod){
    LL res = 1ll;
    while(x){
        if(x&1) res = res*a%mod;
        a = a*a%mod;
        x >>= 1;
    }
    return res;
}
void init(){                              //预处理G()和S()函数的小范围前缀和
    phi[1] = 1; int cnt = 0;
    for(int i = 2;i < maxn; ++i){
        if(!phi[i]) phi[i] = i-1,prime[cnt++] = i;
        for(int j = 0;j<cnt&&prime[j]*i<maxn; ++j){
            if(i%prime[j] == 0){
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
            else phi[i*prime[j]] = phi[i] * phi[prime[j]];
        }
    }
    for(int i = 1;i < maxn; ++i)
    for(int j = i;j < maxn; j += i)
        d[j]++;
    for(int i = 2;i < maxn; ++i){
        phi[i] += phi[i-1];
        if(phi[i] >= p) phi[i] -= p;
        d[i] = d[i-1]+d[i]*i%p;
        if(d[i] >= p) d[i] -= p;
    }
}
LL Sum(int n){                        //计算S(n)
    if(n < maxn) return phi[n];
    if(mp.count(n)) return mp[n];
    LL res = 0;
    for(int i = 2,up;i <= n; i=up+1){
        int t = n/i; up = n/t;
        res += 1ll*(up-i+1)*Sum(t)%p;
        if(res >= p) res -= p;
    }
    return mp[n] = (1ll*n*(n+1)/2-res+p)%p;
}
LL cal(int n){                        //计算G(n)
    if(n < maxn) return d[n];
    LL res = 0;
    for(int i = 1,up;i <= n; i=up+1){
        int t = n/i; up = n/t;
        res += 1ll*t*(t+1)/2%p*(1ll*(up+i)*(up-i+1)/2%p)%p;
        if(res >= p) res -= p;
    }
    return res;
}
LL solve(int n){
    LL res = 0,per = 0;
    for(int i = 1,up;i <= n; i=up+1){
        int t = n/i; up = n/t;
        LL tep = cal(up);
        res += (tep-per+p)*(Sum(t)*2-1+p)%p;
        if(res >= p) res -= p; 
        per = tep;
    }
    return res;
}
int main(){
    cin >> n >> m >> p; p--; 
    if(m % p == 0) return puts("0"),0;
    else init();
    cout << qpow(m,solve(n),p+1) << endl;
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值