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

题意:

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

Solution:

有一条结论

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

那么即计算
∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ g c d ( 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 = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j ∑ d ∣ g c d ( 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 , i x,i x,i是因子和因子的因子,优先枚举更因子的因子 d d d,这样 x x x就是 d d d的倍数, i i i就是 x x x的倍数,
∑ d = 1 n μ ( d ) ( ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n i d ⌋ 1 ) ( ∑ i = 1 ⌊ m d ⌋ ∑ j = 1 ⌊ m i d ⌋ 1 ) = ∑ d = 1 n μ ( d ) ( ∑ i = 1 ⌊ n d ⌋ ⌊ n i d ⌋ ) ( ∑ i = 1 ⌊ m d ⌋ ⌊ m i d ⌋ ) (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 = 1 x ⌊ x i ⌋ f(x)=\sum_{i=1}^{x}\lfloor\frac{x}{i}\rfloor f(x)=i=1xix
原式即
∑ d = 1 n μ ( d ) f ( ⌊ n d ⌋ ) f ( ⌊ m d ⌋ ) \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 ( T n ) O(Tn) O(Tn),发现每个 f ( x ) f(x) f(x)可以预处理出来,那么时间复杂度就降到了 O ( n n + T n ) O(n\sqrt{n}+T\sqrt{n}) O(nn +Tn )

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

// #include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(算法)