题解 P3327 【[SDOI2015]约数个数和】
写在前面
貌似没有题解证明了如下公式:
d(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j} [\gcd(x,y)=1] 于是在此篇题解的Extended
部分我会给出一种证明!
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3994
求解(多组数据)
数据范围:
Solution
首先给出一个公式:
因此所求为
改变求和顺序,先枚举因数
将
开始莫比乌斯反演!设
则有
我们把
再根据
又因为
故
接下来再考虑如何求
时间复杂度:
Code
#include <cstdio>
#include <algorithm>
const int N=5e4+5;
int tot,mu[N],p[N];
long long s[N];
bool flg[N];
void init() {
mu[1]=1;
for(int i=2;i<=5e4;++i) {
if(!flg[i]) p[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*p[j]<=5e4;++j) {
flg[i*p[j]]=1;
if(i%p[j]==0) {
mu[i*p[j]]=0;
break;
} else {
mu[i*p[j]]=-mu[i];
}
}
}
for(int i=1;i<=5e4;++i) mu[i]+=mu[i-1];
for(int x=1;x<=5e4;++x) {
long long res=0;
for(int i=1,j;i<=x;i=j+1) j=x/(x/i),res+=1LL*(j-i+1)*(x/i);
s[x]=res;
}
}
int main() {
init();
int T;
for(scanf("%d",&T);T--;) {
int n,m;
scanf("%d%d",&n,&m);
if(n>m) n^=m^=n^=m;
long long ans=0;
for(int i=1,j;i<=n;i=j+1) {
j=std::min(n/(n/i),m/(m/i));
ans+=1LL*(mu[j]-mu[i-1])*s[n/i]*s[m/i];
}
printf("%lld\n",ans);
}
return 0;
}
Extended
如何证明 Solution
中的公式?
我们考虑把每个因子一一映射。
如果
- 如果
c\leqslant a ,那么在i 中选择。 - 如果
c>a ,那么我们把c 减去a ,在j 中选择p^{c-a} (在j 中选择p^e 表示的是p^{a+e} )
对于
通过如上过程,我们发现:对于
等式得证。