洛谷P3327 莫比乌斯反演,约数函数结论

题意:

给出n,mn,mn,m,计算
∑i=1n∑j=1md(ij) \sum_{i=1}^{n}\sum_{j=1}^{m}d(ij) i=1nj=1md(ij)
其中d(x)d(x)d(x)xxx的约数个数

Solution:

有一条结论

d(ij)=∑x∣i∑y∣j[gcd(x,y)=1] d(ij)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] d(ij)=xiyj[gcd(x,y)=1]

那么即计算
∑i=1n∑j=1m∑x∣i∑y∣j[gcd(x,y)=1] \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] i=1nj=1mxiyj[gcd(x,y)=1]
莫比乌斯反演得到
∑i=1n∑j=1m∑x∣i∑y∣j∑d∣gcd(x,y)μ(d) \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}\sum_{d|gcd(x,y)}\mu(d) i=1nj=1mxiyjdgcd(x,y)μ(d)
优先枚举因子,这里有x,ix,ix,i是因子和因子的因子,优先枚举更因子的因子ddd,这样xxx就是ddd的倍数,iii就是xxx的倍数,
∑d=1nμ(d)(∑i=1⌊nd⌋∑j=1⌊nid⌋1)(∑i=1⌊md⌋∑j=1⌊mid⌋1)=∑d=1nμ(d)(∑i=1⌊nd⌋⌊nid⌋)(∑i=1⌊md⌋⌊mid⌋)(1) \sum_{d=1}^{n}\mu(d)(\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{id}\rfloor}1)(\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{id}\rfloor}1)=\sum_{d=1}^{n}\mu(d)(\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{n}{id}\rfloor)(\sum_{i=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{m}{id}\rfloor) \tag{1} d=1nμ(d)(i=1dnj=1idn1)(i=1dmj=1idm1)=d=1nμ(d)(i=1dnidn⌋)(i=1dmidm⌋)(1)

f(x)=∑i=1x⌊xi⌋ f(x)=\sum_{i=1}^{x}\lfloor\frac{x}{i}\rfloor f(x)=i=1xix
原式即
∑d=1nμ(d)f(⌊nd⌋)f(⌊md⌋) \sum_{d=1}^{n}\mu(d)f(\lfloor\frac{n}{d}\rfloor)f(\lfloor\frac{m}{d}\rfloor) d=1nμ(d)f(⌊dn⌋)f(⌊dm⌋)
显然这个式子可以数论分块,每个f(x)f(x)f(x)也可以数论分块,此时总复杂度是O(Tn)O(Tn)O(Tn),发现每个f(x)f(x)f(x)可以预处理出来,那么时间复杂度就降到了O(nn+Tn)O(n\sqrt{n}+T\sqrt{n})O(nn+Tn)

(1)(1)(1)式需要注意的地方是,枚举ddd的倍数作为xxxxxx的倍数作为iii(这里的x,ix,ix,i都是对(1)(1)(1)式之前而言的),一开始做的时候把iii看成比xxx大的ddd的倍数就可以了,实际上这是不一定的,比如说x=3d,i=7dx=3d,i=7dx=3d,i=7dxxx并不能整除iii,所以(1)(1)(1)式的左式的每个括号内的第二个求和的上标发生了变化。可能是练太少了,一些细节还是难注意到

// #include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<bitset>
#include<map>
using namespace std;
using ll=long long;
const int N=5e4+5;

int mu[N],prime[N],cnt,sum[N];
bool nt[N];

ll f[N];

ll calc(int x)
{
    ll ret=0;
    for(int l=1,r;l<=x;l=r+1)
    {
        r=x/(x/l);
        ret+=x/l*(r-l+1);
    }
    return ret;
}

void make_prime()
{
    mu[1]=1;
    for(int i=2;i<=50000;i++)
    {
        if(!nt[i]) prime[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&i*prime[j]<=50000;j++)
        {
            nt[i*prime[j]]=true;
            if(i%prime[j]==0) break;
            else mu[i*prime[j]]=mu[i]*mu[prime[j]];
        }
    }
    for(int i=1;i<=50000;i++)
    {
        sum[i]=sum[i-1]+mu[i];
        f[i]=calc(i);
    }
}

int main()
{
    make_prime();
    int t; cin>>t;
    while(t--)
    {
        ll ans=0;
        int n,m; cin>>n>>m;
        if(n>m) swap(n,m);
        for(int l=1,r;l<=n;l=r+1)
        {
            r=min(n/(n/l),m/(m/l));
            ans+=f[n/l]*f[m/l]*(sum[r]-sum[l-1]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值