BZOJ传送门
洛谷传送门
解析:
当然这道题有常数十分优秀的容斥 O ( n log n ) O(n\log n) O(nlogn)做法:https://blog.csdn.net/zxyoi_dreamer/article/details/82289575
以及复杂度十分优秀的
O
(
n
)
O(n)
O(n)~
O
(
n
)
O(\sqrt n)
O(n)做法。
(这个符号就是指
O
(
n
)
O(n)
O(n))预处理,
O
(
n
)
O(\sqrt n)
O(n)回答每个询问
思路:
O ( n ) O(n) O(n)做法就是莫比乌斯反演,不过因为线性筛的较大常数被容斥做法吊打。
要不开一下 1 e 7 1e7 1e7?
首先将所有格子上的数+1可以发现格子上的数就是行和列的 g c d × 2 gcd\times 2 gcd×2
那么问题就是这个了: ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) \sum_{i=1}^n\sum_{j=1}^mgcd(i,j) i=1∑nj=1∑mgcd(i,j)
莫比乌斯反演,首先大力推式子:
∑ d d ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] = ∑ d d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] = ∑ d d ( ∑ T = 1 ⌊ min ( n , m ) d ⌋ ⌊ n d T ⌋ ⌊ m d T ⌋ μ ( T ) ) \begin{aligned} &\sum_{d}d\sum_{i=1}^{n}\sum_{j=1}^m[gcd(i,j)=d]\\ =&\sum_{d}d\sum_{i=1}^{\lfloor\frac{n}d\rfloor}\sum_{j=1}^{\lfloor\frac{m}d\rfloor}[gcd(i,j)=1]\\ =&\sum_{d}d(\sum_{T=1}^{\lfloor\frac{\min(n,m)}{d}\rfloor}\lfloor\frac{n}{dT}\rfloor\lfloor\frac{m}{dT}\rfloor\mu(T)) \end{aligned} ==d∑di=1∑nj=1∑m[gcd(i,j)=d]d∑di=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)=1]d∑d(T=1∑⌊dmin(n,m)⌋⌊dTn⌋⌊dTm⌋μ(T))
可以 O ( n n ) O(n\sqrt n) O(nn)回答每个询问了,AC此题已经没问题了。
但是我们的目标是 O ( n ) O(\sqrt n) O(n)回答每个询问,继续化简:
考虑改变枚举顺序,这次变动有点大,请读者仔细观察
A n s = ∑ t = 1 min ( n , m ) ⌊ n t ⌋ ⌊ m t ⌋ ∑ D ∣ t μ ( D ) t D \begin{aligned} Ans=\sum_{t=1}^{\min(n,m)}\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}t\rfloor\sum_{D\mid t}\mu(D)\frac{t}D \end{aligned} Ans=t=1∑min(n,m)⌊tn⌋⌊tm⌋D∣t∑μ(D)Dt
然后后面这个东西: ∑ D ∣ t μ ( D ) t D \sum_{D\mid t}\mu(D)\frac{t}{D} D∣t∑μ(D)Dt,学过 D i r i c h l e t Dirichlet Dirichlet卷积的都知道, μ ∗ I d = ϕ \mu*Id=\phi μ∗Id=ϕ,所以这个就是个欧拉函数。
所以我们要求的东西 ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) = ∑ t = 1 min ( n , m ) ⌊ n t ⌋ ⌊ m t ⌋ ϕ ( t ) \sum_{i=1}^n\sum_{j=1}^mgcd(i,j)=\sum_{t=1}^{\min(n,m)}\lfloor\frac{n}t\rfloor\lfloor\frac{m}t\rfloor\phi(t) i=1∑nj=1∑mgcd(i,j)=t=1∑min(n,m)⌊tn⌋⌊tm⌋ϕ(t)
于是就可以整除分块了,维护一下 ϕ \phi ϕ的前缀和就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define cs const
cs int P=100005;
int prime[P],pcnt,phi[P];
bool mark[P];
inline void linear_sieves(int len=P-5){
phi[1]=1;
for(int re i=2;i<=len;++i){
if(!mark[i])prime[++pcnt]=i,phi[i]=i-1;
for(int re j=1;i*prime[j]<=len;++j){
mark[i*prime[j]]=true;
if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int n,m;
ll ans;
signed main(){
linear_sieves();
scanf("%d%d",&n,&m);
for(int re i=1;i<=min(n,m);++i)ans+=(ll)(n/i)*(m/i)*phi[i];
cout<<2*ans-(ll)m*n;
return 0;
}