[BZOJ4176]Lucas的数论(莫比乌斯反演+杜教筛)

题目描述

传送门

题解

做约数个数和的时候有一个结论:

d(nm)=i|nj|m[(i,j)=1]

直接套进去
i=1nj=1mx|iy|j[(x,y)=1]

然后根据反演公式 [n=1]=d|nμ(d)
=d=1nμ(d)x=1n[d|x]i=1n[x|i]y=1n[d|y]j=1n[y|j]

x=dx,y=dy ,再令 i=di,j=dj
=d=1nμ(d)(x=1ndi=1nd[x|i])2

=d=1nμ(d)(x=1ndndx)2

f(n)=i=1nni ,这个可以在 O(n) 的时间内算出
那么原式
=d=1nμ(d)f(nd)

不加求 μ 时间是 O(n34) ?不大会算…写出来跑了跑感觉稳
用杜教筛预处理 μ 然后就可以做了
首先线筛出来 n34 μ ,然后如果 ni n34 之内就可以直接查询,但是如果不在范围内就需要查询一下了。不过可以发现不在范围内的数非常有限,找出这些数了之后从小到大求,记录一个数组sum(d)表示 [1...nd] μ 的和,这样就可以做到 O(1) 查询了
时间似乎还是 O(n34)

代码

#include
#include
#include
#include
#include
#include
using namespace std;
#define Mod 1000000007
#define LL long long
#define N 10000005

int n,k=1000000;
int prime[N],p[N],mu[N];
LL ans,sum[N];

void get()
{
    mu[1]=1;
    for (int i=2;i<=k;++i)
    {
        if (!p[i])
        {
            prime[++prime[0]]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=prime[0]&&i*prime[j]<=k;++j)
        {
            p[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            else mu[i*prime[j]]=-mu[i];
        }
    }
    for (int i=1;i<=k;++i) mu[i]+=mu[i-1];
}
LL getmu(int x)
{
    if (x<=k) return mu[x];
    return sum[n/x];
}
void summu()
{
    int t;
    for (t=1;n/t>k;++t);
    for (;t;--t)
    {
        int m=n/t;
        sum[t]=1;
        for (int i=2,j=0;i<=m;i=j+1)
        {
            j=m/(m/i);
            sum[t]-=getmu(m/i)*(LL)(j-i+1);
            sum[t]%=Mod;
        }
    }
}
LL f(int n)
{
    LL ans=0;
    for (int i=1,j=0;i<=n;i=j+1)
    {
        j=n/(n/i);
        ans+=(LL)(j-i+1)*(n/i)%Mod;
        ans%=Mod;
    }
    return ans*ans%Mod;
}
int main()
{
    scanf("%d",&n);
    get();summu();
    for (int i=1,j=0;i<=n;i=j+1)
    {
        j=n/(n/i);
        ans+=f(n/i)*(getmu(j)-getmu(i-1))%Mod;
        ans%=Mod;
    }
    ans=(ans+Mod)%Mod;
    printf("%lld\n",ans);
}

你可能感兴趣的:(题解,莫比乌斯反演)