看绝大多数题解都是莫反,这里写个不用莫反的(其实就是推式子)
∑
i
=
1
n
∑
j
=
1
m
τ
(
i
)
τ
(
j
)
τ
(
gcd
(
i
,
j
)
)
\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\tau(i)\tau(j)\tau(\gcd(i,j))
i=1∑nj=1∑mτ(i)τ(j)τ(gcd(i,j))
∑
i
=
1
n
τ
(
i
)
∑
j
=
1
m
τ
(
j
)
τ
(
gcd
(
i
,
j
)
)
\sum\limits_{i=1}^{n}\tau(i)\sum\limits_{j=1}^{m}\tau(j)\tau(\gcd(i,j))
i=1∑nτ(i)j=1∑mτ(j)τ(gcd(i,j))
∑
i
=
1
n
τ
(
i
)
∑
j
=
1
m
τ
(
j
)
∑
k
∣
i
,
k
∣
j
1
\sum\limits_{i=1}^{n}\tau(i)\sum\limits_{j=1}^{m}\tau(j)\sum\limits_{k|i,k|j}1
i=1∑nτ(i)j=1∑mτ(j)k∣i,k∣j∑1
接下来优先枚举
k
k
k
∑
k
=
1
min
(
n
,
m
)
∑
k
∣
i
,
i
≤
n
τ
(
i
)
∑
k
∣
j
,
j
≤
m
τ
(
j
)
\sum\limits_{k=1}^{\min(n,m)}\sum\limits_{k|i,i\le n}\tau(i)\sum\limits_{k|j,j \le m}\tau(j)
k=1∑min(n,m)k∣i,i≤n∑τ(i)k∣j,j≤m∑τ(j)
设
s
k
=
∑
k
∣
i
,
i
≤
n
τ
(
i
)
s_k=\sum\limits_{k|i,i\le n}\tau(i)
sk=k∣i,i≤n∑τ(i),
c
k
=
∑
k
∣
i
,
i
≤
m
τ
(
i
)
c_k=\sum\limits_{k|i,i\le m}\tau(i)
ck=k∣i,i≤m∑τ(i)
那么原式等于
∑
k
=
1
min
(
n
,
m
)
s
k
⋅
c
k
\sum\limits_{k=1}^{\min(n,m)}s_k \cdot c_k
k=1∑min(n,m)sk⋅ck
我们可以 O ( n log n ) O(n \log n) O(nlogn) 预处理出 τ ( i ) , s i , c i \tau(i),s_i,c_i τ(i),si,ci,代入上式即可
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=2000000+10;
int f[Maxn];
long long s[Maxn],c[Maxn];
int n,m;
long long p,ans;
inline void check(long long &x,long long y)
{
x=(x+y)%p;
}
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
int main()
{
n=read(),m=read(),p=read();
if(n>m)swap(n,m);
for(int i=1;i<=m;++i)
for(int j=i;j<=m;j+=i)
++f[j];
for(int i=1;i<=n;++i)
{
for(int j=i;j<=n;j+=i)
check(s[i],f[j]);
for(int j=i;j<=m;j+=i)
check(c[i],f[j]);
check(ans,(c[i]*s[i])%p);
}
printf("%lld\n",ans);
return 0;
}