BZOJ 3994: [SDOI2015]约数个数和 莫比乌斯反演

title

BZOJ 3994

LUOGU 3327

Description

\(d(x)\)\(x\) 的约数个数,给定 \(N、M\),求 \(\sum^N_{i=1}\sum^M_{j=1}d(ij)\)\(1\leqslant N,M,T\leqslant 5\times 10^4\)

analysis

首先由一个很神奇的式子开始1
\[ d(i,j)=\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1] \]

因此我们就是在求这个东西了:
\[ \sum^N_{i=1}\sum^M_{j=1}\sum_{x|i}\sum_{y|j}[\gcd(x,y)=1] \]

改变求和顺序,先枚举因数 \(x\)\(y\)
\[ \sum^N_{x=1}\sum^M_{y=1}\lfloor\frac{N}{x}\rfloor\lfloor\frac{M}{y}\rfloor[\gcd(x,y)=1] \]

\(x\)\(y\) 换成 \(i\)\(j\) 吧:
\[ \sum^N_{i=1}\sum^M_{j=1}\lfloor\frac{N}{i}\rfloor\lfloor\frac{M}{j}\rfloor[\gcd(i,j)=1] \]

下面开始莫比乌斯反演,设:
\[ f(x)=\sum^N_{i=1}\sum^M_{j=1}\lfloor\frac{N}{i}\rfloor\lfloor\frac{M}{j}\rfloor[\gcd(i,j)=x]\\ g(x)=\sum_{x|d}f(d) \]

则有:
\[ g(x)=\sum^N_{i=1}\sum^M_{j=1}\lfloor\frac{N}{i}\rfloor\lfloor\frac{M}{j}\rfloor[x|\gcd(i,j)] \]

我们把 \(x\) 提出来就可以消除 \(\gcd\) 的影响了:
\[ g(x)=\sum^{\frac{N}{x}}_{i=1}\sum^{\frac{M}{y}}_{j=1}\lfloor\frac{N}{ix}\rfloor\lfloor\frac{M}{jx}\rfloor \]

再根据 \(f(x)\) 的定义,得到答案为 \(f(1)\) ,又因为:
\[ f(n)=\sum_{n|d}\mu(\frac{d}{n})g(d) \]

所以
\[ f(1)=\sum_{1|d}\mu(\frac{d}{1})g(d)=\sum_{i=1}\mu(i)g(i) \]

接下来再考虑如何求 \(g(x)\),我们可以先计算 \(s(x)=\sum\limits_{i=1}^{x} \left\lfloor\frac{x}{i}\right\rfloor\) ,就可以 \(\Theta(1)\) 计算 \(g(x)\)

时间复杂度: \(\Theta(T\sqrt{n})\)

code

#include<bits/stdc++.h>

typedef long long ll;
const int maxn=5e4;
typedef int iarr[maxn+10];

namespace IO
{
    char buf[1<<15],*fs,*ft;
    inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
    template<typename T>inline void read(T &x)
    {
        x=0;
        T f=1, ch=getchar();
        while (!isdigit(ch) && ch^'-') ch=getchar();
        if (ch=='-') f=-1, ch=getchar();
        while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
        x*=f;
    }

    char Out[1<<24],*fe=Out;
    inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
    template<typename T>inline void write(T x,char str)
    {
        if (!x) *fe++=48;
        if (x<0) *fe++='-', x=-x;
        T num=0, ch[20];
        while (x) ch[++num]=x%10+48, x/=10;
        while (num) *fe++=ch[num--];
        *fe++=str;
    }
}

using IO::read;
using IO::write;

iarr mu,d,cnt,prime;
bool isnotpr[maxn+10];
int tot;
inline void pre()
{
    mu[1]=d[1]=1;
    for (int i=2; i<=maxn; ++i)
    {
        if (!isnotpr[i]) prime[++tot]=i, mu[i]=-1, d[i]=2, cnt[i]=1;
        for (int j=1; prime[j]*i<=maxn; ++j)
        {
            isnotpr[prime[j]*i]=1;
            if (i%prime[j]==0)
            {
                mu[prime[j]*i]=0, d[prime[j]*i]=d[i]/(cnt[i]+1)*(cnt[i]+2), cnt[prime[j]*i]=cnt[i]+1;
                break;
            }
            mu[prime[j]*i]=-mu[i], d[prime[j]*i]=d[i]<<1, cnt[prime[j]*i]=1;
        }
    }
    for (int i=1; i<=maxn; ++i) mu[i]+=mu[i-1], d[i]+=d[i-1];
}

int main()
{
    pre();
    int T;read(T);
    while (T--)
    {
        int n,m;read(n);read(m);
        if (n>m) std::swap(n,m);
        ll ans=0;
        for (int l=1,r; l<=n; l=r+1)
        {
            r=std::min(n/(n/l),m/(m/l));
            ans+=1ll*(mu[r]-mu[l-1])*d[n/l]*d[m/l];
        }
        write(ans,'\n');
    }
    IO::flush();
    return 0;
}

  1. 这个式子的证明请看 Siyuan

猜你喜欢

转载自www.cnblogs.com/G-hsm/p/11417797.html