jzoj4161 于神之怒

本文介绍了一种涉及最大公约数(gcd)的问题解决思路,通过莫比乌斯反演进行数学推导,最终实现了算法优化。文章详细展示了如何通过数论分块和线性筛预处理来降低算法复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面:
这里写图片描述

输入:
一行三个数,n, m, k。

输出:

数据范围:

Solution:
(gcd)遇到这种题首先考虑莫比乌斯反演(谁叫有gcd呢)
Ans=ni=1mj=1gcd(i,j)k先写出式子:Ans=∑i=1n∑j=1mgcd(i,j)k
n<=m,i,jgcd,:尝试化简:先假设n<=m,将枚举i,j变为枚举gcd,现在问题转变为:
Ans=nd=1dkni=1mj=1[gcd(i,j)==d]Ans=∑d=1ndk∑i=1n∑j=1m[gcd(i,j)==d]
西,,F(d)=ni=1mj=1[d|gcd(i,j)]=ndmd事实上后面这个东西莫比乌斯反演基础题吧,不多讲了,设函数F(d)=∑i=1n∑j=1m[d|gcd(i,j)]=⌊nd⌋⌊md⌋
,Ans=nd=1dkd|iμ(id)ndmd然后反演即可,那么我们可得到式子:Ans=∑d=1ndk∑d|iμ(id)⌊nd⌋⌊md⌋
Ans=nd=1dkndi=1μ(i)nidmid改变枚举:Ans=∑d=1ndk∑i=1⌊nd⌋μ(i)⌊nid⌋⌊mid⌋
nd,ndmd,.此时枚举的⌊nd⌋可以数论分块,对于右边的因为已知ndmd,所以仍然可以数论分块.
线O(n).我们两次分块,再用线性筛预处理莫比乌斯函数,就可以把复杂度降为O(n)了.
T=id,T:此时其实我们可以继续化简,设T=id,枚举T:
Ans=nT=1nTmTd|Tdkμ(Td)Ans=∑T=1n⌊nT⌋⌊mT⌋∑d|Tdkμ(Td)
dk,,:可以发现后面这一部分如果把dk替换成函数,那么是狄利克雷卷积的形式,根据定理:
:T线两个积性函数的狄利克雷卷积也是积性函数:我们就可以把每个T对应的值用线性筛预处理出来
,O(n)这样就可以应对多组数据,每次O(n)复杂度了
,至于那个积性函数的预处理,具体看代码吧

代码:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 5e6 + 5;
typedef long long ll;
# define mod 1000000007
ll prime[N],sum[N],f[N],g[N];
bool is[N];
int n,m,k,tot;
ll ans;
inline ll pow(ll x,int p)
{
    ll sum = 1;
    while (p)
    {
        if (p & 1) sum = (sum * x) % mod;
        x = (x * x) % mod; p >>= 1;
    }
    return sum;
}
inline void pre()
{
    f[1] = 1;
    for (int i = 2;i <= N - 5; ++i)
    {
        if (!is[i]) { g[i] = pow(i * 1ll,k); f[i] = g[i] >= 1 ? g[i] - 1 : g[i] - 1 + mod; prime[++tot] = i; }
        for (int j = 1;j <= tot && prime[j] * i <= N - 5; ++j)
        {
            is[prime[j] * i] = 1;
            if (i % prime[j] == 0)
            {
                f[prime[j] * i] = (f[i] * g[prime[j]]) % mod;
                break;
            }
            f[prime[j] * i] = (f[i] * f[prime[j]]) % mod;
        }
    }
    for (int i = 1;i <= N - 5; ++i) sum[i] = (sum[i - 1] + f[i]) % mod;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    pre();
    if (n > m) swap(n,m);
    for (int l = 1,r; l <= n; l = r + 1)
    {
        r = min(n,min(n / (n / l),m / (m / l)));
        ans = (ans + (n / l) * 1ll * (m / l) % mod * 1ll * (sum[r] - sum[l - 1] + mod)) % mod;
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值