题目描述
推一下
∑i=1n∑j=1mμ(i)∗μ(j)∗∑d|ijd
对于每一个d|ij,一定可以把d拆成d=ab满足a|i且b|j,我们可以考虑枚举a和b。因为一个d有多种拆法,为了避免重复,需保证(a,j/b)=1,因为如果(a,j/b)=k>1的话,a/k和bk也是一种合法答案。
还记得莫比乌斯函数的一个性质么?
∑d|nμ(d)
当n等于1时这个式子值为1否则值为0。
∑i=1n∑j=1mμ(i)∗μ(j)∗∑a|i,b|ja∗jb∗∑d|a,d|bμ(d)
我们设
f(a)=∑a|ia∗μ(i)
g(b)=∑b|jjb∗μ(j)
f与g我们可以预处理,接着把答案式子交换主体,再代入f与g于是式子变成
∑d=1nμ(d)∗(∑d|af(a))∗(∑d|bg(b))
那就可以算啦
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=500000+10,mo=998244353;
int f[maxn],g[maxn],miu[maxn],pri[maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,top,ans;
int main(){
scanf("%d%d",&n,&m);
if (n>m) swap(n,m);
miu[1]=1;
fo(i,2,m){
if (!bz[i]){
miu[i]=-1;
pri[++top]=i;
}
fo(j,1,top){
if (pri[j]>m/i) break;
bz[i*pri[j]]=1;
if (i%pri[j]==0){
miu[i*pri[j]]=0;
break;
}
miu[i*pri[j]]=-miu[i];
}
}
fo(i,1,n)
fo(j,1,n/i)
(f[i]+=i*miu[i*j])%=mo;
fo(i,1,m)
fo(j,1,m/i)
(g[i]+=j*miu[i*j])%=mo;
fo(i,1,n){
k=0;
fo(j,1,n/i) (k+=f[i*j])%=mo;
l=0;
fo(j,1,m/i) (l+=g[i*j])%=mo;
(ans+=(ll)miu[i]*k*l%mo)%=mo;
}
(ans+=mo)%=mo;
printf("%d\n",ans);
}