题意很好理解。
参考博客 https://blog.sengxian.com/solutions/bzoj-3994
下边再说一下思路。
首先,借助约数定理证明
然后借助莫比乌斯函数的性质及一系列的求和顺序变换,得到
最后定义D(x)为d(x)的前缀和,那么最后那个式子可以化为 sigma mu[x]*D[n/x]*D[m/x] (x=1…min(n,m)),这时就可以借助分块+预处理前缀和的方法来求ans了。
ps. 因为d[x]是积性函数,所以可以用线性筛来处理。
for (int i=1, last=1; i<=n; i=last+1) { last = min(n / (n / i), m / (m / i)); ans += (LL)(sum[last] - sum[i - 1]) * (n / i) * (m / i); }
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=5e5+5; int prime[maxn+5]; bool check[maxn+5]; int d[maxn+5]; int mu[maxn+5]; LL D[maxn+5]; int MU[maxn+5]; int min_prime_cnt[maxn+5]; void init() { mu[1]=1,d[1]=1; int tot=0; for(int i=2; i<=maxn; i++) { if(!check[i]) { prime[tot++]=i; mu[i]=-1; min_prime_cnt[i]=1; d[i]=2; } for(int j=0; j<tot; j++) { int t=i*prime[j]; if(t>maxn) break; check[t]=true; if(i%prime[j]==0) { mu[t]=0; min_prime_cnt[t]=min_prime_cnt[i]+1; d[t]=d[i]/(min_prime_cnt[i]+1)*(min_prime_cnt[t]+1); break; } else { mu[t]=-mu[i]; d[t]=d[i]*2; min_prime_cnt[t]=1; } } } for(int i=1; i<=maxn; i++) { MU[i]=MU[i-1]+mu[i]; D[i]=D[i-1]+d[i]; } } int n,m; int main() { init(); int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); LL ans=0; for (int i=1, last=1; i<=min(n,m); i=last+1) { last = min(n / (n / i), m / (m / i)); ans += (LL)(MU[last]-MU[i-1])*D[n/i]*D[m/i]; } printf("%lld ",ans); } }