小A的数学题(莫比乌斯反演&数论)

小A的数学题(莫比乌斯反演&数论)_c++
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=1nd2i=1nj=1m[gcd(i,j)=d]

考虑 d d d的次数。
d = 1 d=1 d=1 贡献为 n ∗ m n*m nm
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] dnd2ij[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 =dnd2kμ(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;
}