[bzoj3994] [SDOI2015] 约数个数和

Description

(d(x))(x) 的约数个数,给定 (N)(M),求 (sumlimits_{i=1}^N sumlimits_{j=1}^M d(ij))

Input

输入文件包含多组测试数据。
第一行,一个整数 (T) ,表示测试数据的组数。
接下来的 (T) 行,每行两个整数 (N)(M)

Output

(T) 行,每行一个整数,表示你所求的答案。

Sample Input

2

7 4

5 6

Sample Output

110

121

HINT

(1<=N, M<=50000)
(1<=T<=50000)


想法

首先有一个结论
$ d(i,j)=sumlimits_{x|i} sumlimits_{y|j} [gcd(x,y)==1] $
原谅我不会证 (qwq)

接下来,喜闻乐见的推式子

[egin{equation*} egin{aligned} &sumlimits_{i=1}^n sumlimits_{j=1}^m d(ij) \ =&sumlimits_{i=1}^n sumlimits_{j=1}^m sumlimits_{x|i} sumlimits_{y|j} [gcd(x,y)==1] \ =&sumlimits_{i=1}^n sumlimits_{j=1}^m [gcd(x,y)==1] imes lfloor frac{n}{x} floor imes lfloor frac{m}{y} floor \ end{aligned} end{equation*} ]

(f(i)=sumlimits_{i=1}^n sumlimits_{j=1}^m [gcd(x,y)==1] imes lfloor frac{n}{x} floor imes lfloor frac{m}{y} floor)
应用莫比乌斯反演

[egin{equation*} egin{aligned} F(i)&=f(i)+f(2i)+... \ &=sumlimits_{x=1}^{lfloor frac{n}{i} floor} sumlimits_{y=1}^{lfloor frac{m}{i} floor} lfloor frac{n}{ix} floor lfloor frac{m}{iy} floor \ &=sumlimits_{x=1}^{lfloor frac{n}{i} floor} lfloor frac{n}{ix} floor sumlimits_{y=1}^{lfloor frac{m}{i} floor} lfloor frac{m}{iy} floor end{aligned} end{equation*} ]

不妨设 (s(x)=sumlimits_{i=1}^x lfloor frac{x}{i} floor)
它可以预处理后 (O(1)) 调用
(f(i)=sumlimits_{i|d} mu(frac{d}{i}) s(frac{n}{d}) s(frac{m}{d}))
那么

[egin{equation*} egin{aligned} ans&=f(1) \ &=sumlimits_{i=1}^{min(n,m)} mu(i) s(frac{n}{i})s(frac{m}{i}) end{aligned} end{equation*} ]

然后就好啦。


代码

#include<cstdio>
#include<iostream>
#include<algorithm>
 
using namespace std;
 
const int N = 50005;
typedef long long ll;
 
ll s[N];
int mu[N],p[N],prime[N],pnum,sum[N];
void getp(){
    mu[1]=sum[1]=1;
    for(int i=2;i<N;i++) p[i]=1;
    for(int i=2;i<N;i++){
        if(p[i]) prime[pnum++]=i,mu[i]=-1;
        for(int j=0;j<pnum && (ll)prime[j]*i<N;j++){
            p[i*prime[j]]=0;
            if(i%prime[j]==0){
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
        sum[i]=sum[i-1]+mu[i];
    }
}
void gets(){
    for(int x=1;x<N;x++){
        for(int l=1,r;l<=x;l=r+1){
            r=x/(x/l);
            s[x]+=1ll*(x/l)*(r-l+1);
        }
    }
}
 
int main()
{
    int T,n,m;
    getp(); gets();
     
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        ll ans=0;
        for(int l=1,r;l<=n && l<=m;l=r+1){
            r=min(n/(n/l),m/(m/l));
            ans+=s[n/l]*s[m/l]*(sum[r]-sum[l-1]);
        }
        printf("%lld
",ans);
    }
     
    return 0;
}

既然选择了远方,便只顾风雨兼程
原文地址:https://www.cnblogs.com/lindalee/p/10539858.html