显然
横着或竖着共有\(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} \rfloor}{(n-i*d)}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}{(m-j*d)}
\\=\sum_{d=1}^{n}\varphi(d)*(n*\lfloor \frac{n}{d} \rfloor-d*\frac{\lfloor \frac{n}{d} \rfloor*(\lfloor \frac{n}{d} \rfloor-1)}{2})*(m*\lfloor \frac{m}{d} \rfloor-d*\frac{\lfloor \frac{m}{d} \rfloor*(\lfloor \frac{m}{d} \rfloor-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\n",(ans*2%p+m*C3(n)%p+n*C3(m)%p)%p);
return 0;
}
原文:https://www.cnblogs.com/yicongli/p/10165502.html