[2018雅礼集训1-10]Function 积性函数前缀和

题面
题目要我们求

i=1nd|iμ(d)σ20(id)

f(i)=d|iμ(d)σ20(id) ,感觉 f 会是个很优美的东西,打表发现
f(i)=σ0(i2)

怎么证明呢?考虑 σ0(i2)=d|iμ(d)σ20(id) 用莫比乌斯反演反过来就是 σ20(n)=d|nσ0(n2) 。这里给出一种推式子的证法:
d|nσ0(d2)=d|ni|dj|d[(i,j)=1]=d|ni|dj|d[(ind,jnd)=nd]

=d|ni|nj|n[(i,j)=nd]=d|ni|nj|n[(i,j)=d]

其中第一步利用了 σ0(n2) 等于 n 的有序互质约数对个数,大概考虑 n 的某一个质因数项 pkii ,前者的贡献是 2ki+1 ,后者是 (pi,pj)(i=0||j=0) 的对数,也是 2ki+1
σ20(n)=i|nj|n1=d|ni|nj|n[(i,j)=d]

得证。
然后就和 SPOJ DIVCNT2一样了。
代码:

#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=1000000;
int n,pri[maxn+5],num,mu[maxn+5],d[maxn+5],sm[maxn+5],sd[maxn+5];
bool flag[maxn+5];
void getpri(int n)
{
    memset(flag,1,sizeof(flag));
    flag[1]=0;mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(flag[i]) pri[++num]=i,mu[i]=-1;
        for(int j=1;j<=num&&i*pri[j]<=n;j++)
        {
            flag[i*pri[j]]=0;
            if(i%pri[j]==0) {mu[i*pri[j]]=0;break;}
            mu[i*pri[j]]=-mu[i];
        }
    }
}
ll qsd(int m)
{
    if(m<=maxn) return sd[m];
    ll re=0;
    for(int l=1,r=1,k;l<=m;l=r+1)
    {
        k=m/l;r=m/k;
        re+=(ll)(r-l+1)*k;
    }
    return re;
}
ll qsm(int m)
{
    if(m<=maxn) return sm[m];
    int gen=round(sqrt(m)+1);ll re=0;
    for(int i=1;i<=gen;i++)
        re+=mu[i]*(m/(i*i));    
    return re;  
}
ll solve()
{
    ll re=0;
    for(int l=1,r=1,k;l<=n;l=r+1)
    {
        k=n/l;r=n/k; 
        re+=qsd(k)*(qsm(r)-qsm(l-1));
    }
    return re;
}
int main()
{
    int ca;
    scanf("%d",&ca);
    getpri(maxn);
    for(int i=1;i<=maxn;i++)
        for(int j=1;i*j<=maxn;j++)
            d[i*j]++;       
    for(int i=1;i<=maxn;i++)
        sm[i]=sm[i-1]+mu[i]*mu[i],sd[i]=sd[i-1]+d[i];
    while(ca--)
    {
        scanf("%d",&n);
        printf("%lld\n",solve());
    }
    return 0;
}

你可能感兴趣的:(数论,莫比乌斯反演,杜教筛,积性函数)