bzoj 4176

题意:求$\sum_{i=1}^{n}\sum_{j=1}^{n}d(ij)$

首先推一发式子:

$\sum_{i=1}^{n}\sum_{j=1}^{n}d(ij)$

有一个结论:$d(nm)=\sum_{i|n}\sum_{j|m}[gcd(i,j)\equiv 1]$

然后代入,得:

$\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{p|i}\sum_{q|j}[gcd(p,q)\equiv 1]$

然后优先枚举$p$,$q$,得到:

$\sum_{p=1}^{n}\sum_{q=1}^{n}[gcd(p,q)\equiv 1]\sum_{p|i}\sum_{q|j}1$

那么也就是:

$\sum_{p=1}^{n}\sum_{q=1}^{n}[gcd(p,q)\equiv 1]\frac{n}{p}\frac{n}{q}$

接下来就是常规步骤了

$\sum_{p=1}^{n}\sum_{q=1}^{n}\sum_{t|gcd(p,q)}\mu(t)\frac{n}{p}\frac{n}{p}$

也就是:

$\sum_{t=1}^{n}\mu(t)\sum_{p=1}^{\frac{n}{t}}\frac{n}{pt}\sum_{q=1}^{\frac{n}{t}}\frac{n}{qt}$

 设$f(n)=\sum_{i=1}^{n}\frac{n}{i}$,那么后面就可以变成$\sum_{t=1}^{n}\mu(t)f(\frac{n}{t})^{2}$

求$f$可以数论分块,用杜教筛筛出$\mu$的前缀和之后再套个数论分块即可

代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#define ll long long
using namespace std;
const ll mode=1000000007;
map <ll,ll> S;
int mu[10000005];
ll s[10000005];
int pri[10000005];
int cnt=0;
bool used[10000005];
void init()
{
    mu[1]=s[1]=1;
    for(int i=2;i<=10000000;i++)
    {
        if(!used[i])pri[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt&&i*pri[j]<=10000000;j++)
        {
            used[i*pri[j]]=1;
            if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
            mu[i*pri[j]]=-mu[i];
        }
        s[i]=(s[i-1]+mu[i]+mode)%mode;
    }
}
ll get_S(ll x)
{
    if(x<=10000000)return s[x];
    else if(S.find(x)!=S.end())return S[x];
    ll ret=0;
    int las=0;
    for(int i=2;i<=x;i=las+1)
    {
        las=x/(x/i);
        ll temp=get_S(x/i);
        ret=(ret+temp*(las-i+1)+mode)%mode;
    }
    ret=(1+mode-ret)%mode;
    return S[x]=ret;
}
ll get_sum(ll x)
{
    ll las=0,ret=0;
    for(int i=1;i<=x;i=las+1)
    {
        las=x/(x/i);
        ret+=(las-i+1)*(x/i)%mode;
        ret%=mode;
    }
    return ret*ret%mode;
}
ll solve(ll n)
{
    ll las=0,ret=0;
    for(int i=1;i<=n;i=las+1)
    {
        las=n/(n/i);
        ret+=(get_S(las)-get_S(i-1)+mode)%mode*get_sum(n/i)%mode;
        ret%=mode;
    }
    return ret;
}
int main()
{
    init();
    ll n;
    scanf("%lld",&n);
    printf("%lld\n",solve(n));
    return 0;
}

 

posted @ 2019-07-08 11:54  lleozhang  Views(272)  Comments(0Edit  收藏  举报
levels of contents