小A的数学题(莫比乌斯反演&数论)
1.容斥
原始化简为
∑
d
=
1
n
d
2
∑
i
=
1
n
∑
j
=
1
m
[
g
c
d
(
i
,
j
)
=
d
]
\sum\limits_{d=1}^n d^2\sum_{i=1}^n\sum_{j=1}^m [gcd(i,j)=d]
d=1∑nd2∑i=1n∑j=1m[gcd(i,j)=d]
考虑
d
d
d的次数。
d
=
1
d=1
d=1 贡献为
n
∗
m
n*m
n∗m。
d
=
2
d=2
d=2 贡献为
⌊
n
2
⌋
×
⌊
m
2
⌋
\lfloor\dfrac{n}{2}\rfloor\times \lfloor\dfrac{m}{2}\rfloor
⌊2n⌋×⌊2m⌋
…
\dots
…
这是枚举因数的贡献,因为是最大公因数,所以只需要倒着枚举,然后容斥掉后面较大的贡献即可。
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
ll a[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
ll ans=0;
for(int i=n;i;i--){
a[i]=1LL*(n/i)*(m/i);
for(int j=i*2;j<=n;j+=i){
a[i]-=a[j];
}
ans=(ans+1LL*i*i*a[i])%mod;
}printf("%lld\n",ans);
return 0;
}
方法2:莫比乌斯性质化简
∑
d
n
d
2
∑
i
∑
j
[
g
c
d
(
i
,
j
)
=
1
]
\sum_d^{n} d^2\sum_i\sum_j [gcd(i,j)=1]
∑dnd2∑i∑j[gcd(i,j)=1]
=
∑
d
n
d
2
∑
k
μ
(
k
)
⌊
n
k
d
⌋
⌊
m
k
d
⌋
=\sum_d^{n} d^2\sum_{k} \mu(k) \lfloor\dfrac{n}{kd}\rfloor \ \lfloor\dfrac{m}{kd}\rfloor
=∑dnd2∑kμ(k)⌊kdn⌋ ⌊kdm⌋
后面可以预处理+前缀和+分块优化,然后枚举 d d d即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int p[N],cnt,mu[N],pre[N];
bitset<N>vis;
void getmu(int n){
mu[1]=vis[1]=1;
int cnt=0;
for(int i=2;i<=n;i++){
if(!vis[i]) p[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*p[j]<=n;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
mu[i*p[j]]=0;
break;
}
mu[i*p[j]]=-mu[i];
}
}
for(int i=1;i<=n;i++) pre[i]=pre[i-1]+mu[i];
}
ll solve(int n,int m){
if(n>m) swap(n,m);
ll s=0;
for(int l=1,r;l<=n;l=r+1){
r=min(n/(n/l),m/(m/l));
s+=1LL*(pre[r]-pre[l-1])*(n/l)%mod*(m/l)%mod;
s%=mod;
}
return s;
}
int main(){
getmu(N-5);
int n,m;
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
ll ans=0;
for(int i=1;i<=n;i++){
ans=(ans+1LL*i*i%mod*solve(n/i,m/i)%mod)%mod;
}printf("%lld\n",ans);
return 0;
}