显然
横着或竖着共有(n*(_3^m)+m*(_3^n))种
斜着的方案数为
[sum_{i=1}^{n}sum_{j=1}^{m}{(gcd(i,j)-1)(n-i)(m-j)}
]
就是枚举两个端点
令其中一个为原点
另一个为((i,j))
令(gcd(i,j)=d)
所以最靠近原点的整点是((frac{i}{d},frac{j}{d}))
所以从这个点到((i,j))有(frac{i}{(frac{i}{d})}=d)个整点
除去((i,j))本身
那么他们之间有(gcd(i,j)-1)个整点
然后可以上下左右平移这个线段
答案就是((gcd(i,j)-1)(n-i)(m-j))
考虑如何计算这个式子
把(-1)提出来再反演即可
[sum_{i=1}^{n}sum_{j=1}^{m}{gcd(i,j)(n-i)(m-j)}
\=sum_{i=1}^{n}sum_{j=1}^{m}{sum_{d|gcd(i,j)}{varphi(d)}(n-i)(m-j)}
\=sum_{d=1}^{n}varphi(d)sum_{i=1}^{lfloor frac{n}{d}
floor}{(n-i*d)}sum_{j=1}^{lfloor frac{m}{d}
floor}{(m-j*d)}
\=sum_{d=1}^{n}varphi(d)*(n*lfloor frac{n}{d}
floor-d*frac{lfloor frac{n}{d}
floor*(lfloor frac{n}{d}
floor-1)}{2})*(m*lfloor frac{m}{d}
floor-d*frac{lfloor frac{m}{d}
floor*(lfloor frac{m}{d}
floor-1)}{2})
]
因为提了一个$-1 $
答案先减去(frac{(n-1)*n}{2}+frac{(m-1)*m}{2})
因为端点顺序不固定
答案乘(2)
最后再加上(n*(_3^m)+m*(_3^n))即可
#include "bits/stdc++.h"
using namespace std;
#define ll long long
const int p=1e9+7;
const int N=1e6+7;
int tot;
int pri[N],phi[N];
inline void init(int n){
phi[1]=1;
for(int i=2;i<=n;++i){
if(!phi[i])pri[++tot]=i,phi[i]=i-1;
for(int j=1,tmp;j<=tot&&(tmp=i*pri[j])<=n;++j){
if(i%pri[j]==0){
phi[tmp]=phi[i]*pri[j];
break;
}
phi[tmp]=phi[i]*phi[pri[j]];
}
}
}
inline ll s(ll x){
return (x*(x+1)>>1)%p;
}
inline ll calc(ll n,ll d){
return ((n/d*n-d*s(n/d))%p+p)%p;
}
inline ll C3(ll x){
return (x<3?0:x*(x-1)*(x-2)/6)%p;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
if(n>m)swap(n,m);
ll ans=0;
init(n);
for(int d=1;d<=n;++d){
(ans+=phi[d]*calc(n,d)%p*calc(m,d))%=p;;
}
(ans+=p-s(n-1)*s(m-1)%p)%p;
printf("%lld
",(ans*2%p+m*C3(n)%p+n*C3(m)%p)%p);
return 0;
}