题目描述
题解
首先考虑如何求
d(n,m)
有一个很神的结论:
d(nm)=∑i|n∑j|m[gcd(i,j)=1]
题目要求
∑i=1N∑j=1Md(ij)
令 N<M
可以知道上面那个式子的前缀和即为答案,即
∑i=1N∑j=1Md(ij)=∑n=1N∑m=1M∑i|n∑j|m[gcd(i,j)=1]
那么再化简一下
∑i=1N∑j=1Md(ij)=∑n=1N∑i|n∑m=1M∑j|m∑d=1N[d|i][d|j]μ(d)
观察这个式子,选出来的i其实是[1,N]范围内所有数的约数,会有很多重复的,重复的个数为 ⌊Ni⌋
那么改一下形式
∑i=1N∑j=1Md(ij)=∑i=1N∑j=1M⌊Ni⌋⌊Mj⌋∑d=1N[d|i][d|j]μ(d)
令i=id j=jd
∑i=1N∑j=1Md(ij)=∑d=1Nμ(d)∑i=1Nd∑j=1Md⌊Nid⌋⌊Mjd⌋
∑i=1N∑j=1Md(ij)=∑d=1Nμ(d)∑i=1Nd∑j=1Md⌊Ndi⌋⌊Mdj⌋
设 f(n)=∑i=1n⌊ni⌋
那么
∑i=1N∑j=1Md(ij)=∑d=1Nμ(d)f(Nd)f(Md)
其中f用分块可以在
O(nn−−√)
时间内求出
μ
可以
O(n)
筛出来
但是这道题有多组数据直接暴力枚举d还是不可做。
再观察,
f(Nd)
f(Md)
只有
n−−√+m−−√
种取值,也可以分块来求。
时间复杂度
O(nn−−√+T∗(n−−√+m−−√))
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int N=5e4;
int mu[N+5],p[N+5],prime[N+5],f[N+5];
int T,n,m,p1,p2;
LL ans;
inline void get_mu(){
mu[1]=1;
for (int i=2;i<=N;++i){
if (!p[i]){
prime[++prime[0]]=i;
mu[i]=-1;
}
for (int j=1;j<=prime[0]&&i*prime[j]<=N;++j){
p[i*prime[j]]=1;
if (i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
else mu[i*prime[j]]=-mu[i];
}
}
}
inline int get_f(int n){
int ans=0;
for (int i=1,j;i<=n;i=j+1){
j=n/(n/i);
ans+=(n/i)*(j-i+1);
}
return ans;
}
int main(){
get_mu();
for (int i=1;i<=N;++i) mu[i]+=mu[i-1];
for (int i=1;i<=N;++i)
f[i]=get_f(i);
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&m); if (n>m) swap(n,m);
ans=0; p1=p2=1;
for (int i=1,j;i<=n;i=j+1){
j=min(n/(n/i),m/(m/i));
ans+=(LL)f[n/i]*f[m/i]*(mu[j]-mu[i-1]);
}
printf("%lld\n",ans);
}
}