题目链接
看绝大多数题解都是莫反,这里写个不用莫反的(其实就是推式子)
∑ 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
#include
#include
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;
}