51Nod 1237 最大公约数之和 V3 杜教筛

题目链接http://www.51nod.com/Challenge/Problem.html#!#problemId=1237

题意:求$sum_{i=1}^{n}sum_{j=1}^{n}gcd(i,j)$ ,$1leq{n}leq10^{10}$.

知识提要:$n=sum_{d|n}varphi(d)$ 该公式证明https://blog.csdn.net/chen1352/article/details/50695930

根据莫比乌斯反演可以得到 $varphi(n)=sum_{d|n}mu(frac{n}{d})d$

解析:试着化简这个柿子,枚举最大公因数d

$$ quadsum_{d=1}^{n}sum_{i=1}^{lfloorfrac{n}{d} floor}sum_{j=1}^{lfloorfrac{n}{d} floor}[(i,j)=1]d\$$

$$=sum_{d=1}^{n}dsum_{i=1}^{lfloorfrac{n}{d} floor}sum_{j=1}^{lfloorfrac{n}{d} floor}[(i,j)=1]$$

右边根据莫比乌斯反演化简

$$sum_{d=1}^{n}dsum_{d'=1}^{lfloorfrac{n}{d} floor}mu(d')lfloor{frac{n}{dd'}} floorlfloor{frac{n}{dd'}} floor$$

令T=dd'

$$sum_{T=1}^{n}lfloor{frac{n}{T}} floorlfloor{frac{n}{T}} floorsum_{d|T}mu(frac{T}{d})d$$

把右边替换成$varphi(T)$得到

$$sum_{T=1}^{n}lfloor{frac{n}{T}} floorlfloor{frac{n}{T}} floorvarphi(T)$$

所以求出来欧拉函数的前缀和 这个式子也就求出来了

AC代码

#include <bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define all(a) (a).begin(), (a).end()
#define fillchar(a, x) memset(a, x, sizeof(a))
#define huan printf("
");
#define debug(a,b) cout<<a<<" "<<b<<" ";
using namespace std;
const int maxn=1e6+10,inf=0x3f3f3f3f;
typedef long long ll;
const ll mod = 1000000007;
typedef pair<int,int> pii;
int check[maxn],prime[maxn],phi[maxn],sum[maxn];
void Phi(int N)//莫比乌斯函数线性筛
{
    int pos=0;sum[0]=0;
    sum[1]=phi[1]=1;
    for(int i = 2 ; i <= N ; i++)
    {
        if (!check[i])
            prime[pos++] = i,phi[i]=i-1;
        for (int j = 0 ; j < pos && i*prime[j] <= N ; j++)
        {
            check[i*prime[j]] = 1;
            if (i % prime[j] == 0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
        sum[i]=(sum[i-1]+phi[i])%mod;
    }
}
unordered_map<ll,ll> ma;
ll inv2=500000004;
ll solve(ll n)
{
    if(n<=1e6)
        return sum[n];
    else if(ma.count(n))
        return ma[n];
    ll temp = ((n%mod)*((n+1)%mod)%mod)*inv2%mod;
    for(ll i=2,j;i<=n;i=j+1)
    {
        j=n/(n/i);
        temp = (temp-solve(n/i)*(j-i+1)%mod+mod)%mod;
    }
    return ma[n]=temp;
}
int main()
{
    ll n;
    Phi(1e6);
    scanf("%lld",&n);
    ll ans=0;
    for(ll i=1,j;i<=n;i=j+1)
    {
        j=n/(n/i);
        ll k=(n/i)%mod;
        k = k*k%mod;
        ll temp=((solve(j)-solve(i-1)+mod)%mod)*k%mod;
        ans=(ans+temp)%mod;
    }
    printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/stranger-/p/10762784.html