P3327 [SDOI2015]约数个数和

题意:

设d(x)为x的约数个数,给定 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)

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 d ( i j ) = ∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ g c d ( x , y ) = 1 ] = ∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ x m ⌋ [ g c d ( x , y ) = 1 ] = ∑ x = 1 n ∑ y = 1 m ⌊ n x ⌋ ⌊ x m ⌋ ∑ d ∣ g c d ( x . y ) μ ( d ) = ∑ d = 1 m i n ( n , m ) μ ( d ) ∑ x = 1 ⌊ n d ⌋ ⌊ n d x ⌋ ∑ y = 1 ⌊ m d ⌋ ⌊ m d y ⌋ \sum_{i=1}^{n}\sum_{j=1}^{m}d(ij) \\ =\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x,y)=1] \\ =\sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor\frac{n}{x}\rfloor\lfloor\frac{x}{m}\rfloor[gcd(x,y)=1] \\ =\sum_{x=1}^{n}\sum_{y=1}^{m}\lfloor\frac{n}{x}\rfloor\lfloor\frac{x}{m}\rfloor\sum_{d|gcd(x.y)}\mu(d) \\ =\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^{\lfloor\frac{n}{d}\rfloor}\lfloor\frac{n}{dx}\rfloor\sum_{y=1}^{\lfloor\frac{m}{d}\rfloor}\lfloor\frac{m}{dy}\rfloor i=1nj=1md(ij)=i=1nj=1mxiyj[gcd(x,y)=1]=x=1ny=1mxnmx[gcd(x,y)=1]=x=1ny=1mxnmxdgcd(x.y)μ(d)=d=1min(n,m)μ(d)x=1dndxny=1dmdym
到这里就可以看出用整除分块来求解。

代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>P;
typedef long long ll;
const int N=5e4;
int mu[N+5],prime[N+5],notPrime[N+5],cnt=0,sumMu[N+5];
ll sum[N+5];
void initMu()
{
   
    mu[1]=1;
    for(int i=2;i<=N;i++)
    {
   
        if(!notPrime[i])
        {
   
            prime[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0;j<cnt&&1ll*i*prime[j]<=N;j++)
        {
   
            notPrime[i*prime[j]]=1;
            if(i%prime[j])mu[i*prime[j]]=-mu[i];
            else break;
        }
    }
    for(int i=1;i<=N;i++)
    {
   
        sumMu[i]=sumMu[i-1]+mu[i];
        for(int l=1,r;l<=i;l=r+1)
        {
   
            r=i/(i/l);
            sum[i]+=1ll*i/l*(r-l+1);
        }
    }
}
int n,m,t;
int main()
{
   
    initMu();
    scanf("%d",&t);
    while(t--)
    {
   
        scanf("%d%d",&n,&m);
        ll res=0;
        int M=min(n,m);
        for(int l=1,r;l<=M;l=r+1)
        {
   
            r=min(n/(n/l),m/(m/l));
            res+=1ll*(sumMu[r]-sumMu[l-1])*sum[n/l]*sum[m/l];
        }
        printf("%lld\n",res);
    }
    return 0;
}