我天哪 大大的庆祝一下:
数论黑题 (T1) 达成!
激动地不行
记住套路:乱推 (gcd),欧拉筛模板,然后乱换元,乱换式子,完了整除分块,欧拉筛和前缀和就解决了!
[sum_{i=1}^nsum_{j=1}^msum_{k=1}^pgcd(icdot j,icdot k,jcdot k) imes gcd(i,j,k) imes left(frac{gcd(i,j)}{gcd(i,k) imes gcd(j,k)}+frac{gcd(i,k)}{gcd(i,j) imes gcd(j,k)}+frac{gcd(j,k)}{gcd(i,j) imes gcd(i,k)}
ight)
]
[= sum_{i=1}^n sum_{j=1}^m sum_{k=1}^p frac{gcd(i,j) imes gcd(i,k) imes gcd(j,k)}{gcd(i,j,k)} imes gcd(i,j,k) imes left(frac{gcd(i,j)}{gcd(i,k) imes gcd(j,k)}+frac{gcd(i,k)}{gcd(i,j) imes gcd(j,k)}+frac{gcd(j,k)}{gcd(i,j) imes gcd(i,k)}
ight)
]
[= sum_{i=1}^n sum_{j=1}^m sum_{k=1}^p gcd(i,j)^2 + gcd(i,k)^2 + gcd(j,k)^2
]
[= p imes sum_{i=1}^n sum_{j=1}^m gcd(i,j)^2 + m imes sum_{i=1}^n sum_{k=1}^p gcd(i,k)^2 + n imes sum_{j=1}^m sum_{k=1}^p gcd(j,k)^2
]
下面我们只需考虑 (sum_{i=1}^n sum_{j=1}^m gcd(i,j)^2)的值即可。
[sum_{i=1}^n sum_{j=1}^m gcd(i,j)^2
]
[= sum_{d=1}^{min(n,m)} d^2 sum_{i=1}^n sum_{j=1}^m [gcd(i,j) == d]
]
[= sum_{d=1}^{min(n,m)} d^2 sum_{i=1}^{lfloor frac{n}{d}
floor} sum_{j=1}^{lfloor frac{m}{d}
floor} [gcd(i,j) == 1]
]
这里我们要知道 (sum_{i=1}^a sum_{j=1}^b [gcd(i,j) == 1]).
然后开始莫比乌斯反演。
令:
[f_x = sum_{i=1}^a sum_{j=1}^b [gcd(i,j) == x]
]
[F_x = sum_{i=1}^a sum_{j=1}^b [x | gcd(i,j)]
]
显然有:
[F_x = sum_{x|d} f_d
]
[f_1 = sum_{d=1} F_d imes mu_d
]
[= sum_{d=1}^{min(n,m)} mu_d imes lfloor frac{n}{d}
floor imes lfloor frac{m}{d}
floor
]
回到原式:
[= sum_{d=1}^{min(n,m)} d^2 sum_{i=1}^{lfloor frac{n}{d}
floor} sum_{j=1}^{lfloor frac{m}{d}
floor} [gcd(i,j) == 1]
]
[= sum_{d=1}^{min(n,m)} d^2 sum_{g=1}^{min(lfloor frac{n}{d}
floor,lfloor frac{m}{d}
floor)} mu_d lfloor frac{n}{g imes d}
floor lfloor frac{m}{g imes d}
floor
]
设 (T = g imes d) ,换 (d|T) 枚举。
[= sum_{T=1}^{min(n,m)} lfloor frac{n}{T}
floor imes lfloor frac{m}{T}
floor imes sum_{d|T} d^2 mu{lfloor frac{T}{d}
floor}
]
对于前面的,我们整除分块。
后面的,是个卷积,两个还都是积性函数,用欧拉筛一下。
(欧拉筛能解决所有的积性函数。如果不能,就再筛一遍。)
提醒一句:我们是要对 (n) , (m) ,(p) 求三次,不要只求一次啊。
突然感觉黑题也不怎难,会了套路就好
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7;
const int N=2e7+1;
inline ll read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
ll T,n,m,p,cnt=0; bool h[N];
ll prime[N],sum[N],f[N];
inline ll solve(ll n,ll m) {
ll ans=0;
for(ll i=1,t;i<=min(n,m);i=t+1) {
t=min(n/(n/i),m/(m/i));
ans=(ans+(n/i)*(m/i)%MOD*((sum[t]-sum[i-1]+MOD)%MOD)%MOD)%MOD;
} return ans;
} //整除分块
inline void Euler() {
f[1]=1;
for(ll i=2;i<N;i++) {
if(!f[i]) prime[++cnt]=i,f[i]=(i*i%MOD-1+MOD)%MOD;
for(ll j=1;j<=cnt && i*prime[j]<N;j++) {
f[i*prime[j]]=1;
if(i%prime[j]==0) {
f[i*prime[j]]=f[i]*prime[j]%MOD*prime[j]%MOD;
break;
} else f[i*prime[j]]=f[i]*f[prime[j]]%MOD;
}
}
for(ll i=1;i<N;i++) sum[i]=(sum[i-1]+f[i])%MOD;
} //欧拉筛,做前缀和
int main(){
Euler(); T=read(); while(T--) {
n=read(),m=read(),p=read();
printf("%lld
",(p*solve(n,m)%MOD+m*solve(n,p)%MOD+n*solve(m,p)%MOD)%MOD);
} //别忘了3次轮换
return 0;
}