bzoj 3994 [SDOI2015]约数个数和

题目链接

题意很好理解。

参考博客  https://blog.sengxian.com/solutions/bzoj-3994

下边再说一下思路。

  首先,借助约数定理证明

  然后借助莫比乌斯函数的性质及一系列的求和顺序变换,得到

  最后定义D(x)为d(x)的前缀和,那么最后那个式子可以化为 sigma mu[x]*D[n/x]*D[m/x]      (x=1…min(n,m)),这时就可以借助分块+预处理前缀和的方法来求ans了。

  ps. 因为d[x]是积性函数,所以可以用线性筛来处理。

for (int i=1, last=1; i<=n; i=last+1)
{
    last = min(n / (n / i), m / (m / i));
    ans += (LL)(sum[last] - sum[i - 1]) * (n / i) * (m / i);
}
分块+预处理前缀和模板
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

const int maxn=5e5+5;
int prime[maxn+5];
bool check[maxn+5];
int d[maxn+5];
int mu[maxn+5];
LL D[maxn+5];
int MU[maxn+5];
int min_prime_cnt[maxn+5];

void init()
{
    mu[1]=1,d[1]=1;
    int tot=0;
    for(int i=2; i<=maxn; i++)
    {
        if(!check[i])
        {
            prime[tot++]=i;
            mu[i]=-1;
            min_prime_cnt[i]=1;
            d[i]=2;
        }
        for(int j=0; j<tot; j++)
        {
            int t=i*prime[j];
            if(t>maxn) break;
            check[t]=true;
            if(i%prime[j]==0)
            {
                mu[t]=0;
                min_prime_cnt[t]=min_prime_cnt[i]+1;
                d[t]=d[i]/(min_prime_cnt[i]+1)*(min_prime_cnt[t]+1);
                break;
            }
            else
            {
                mu[t]=-mu[i];
                d[t]=d[i]*2;
                min_prime_cnt[t]=1;
            }
        }
    }
    for(int i=1; i<=maxn; i++)
    {
        MU[i]=MU[i-1]+mu[i];
        D[i]=D[i-1]+d[i];
    }
}

int n,m;

int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        LL ans=0;
        for (int i=1, last=1; i<=min(n,m); i=last+1)
        {
            last = min(n / (n / i), m / (m / i));
            ans += (LL)(MU[last]-MU[i-1])*D[n/i]*D[m/i];
        }
        printf("%lld
",ans);
    }
}
原文地址:https://www.cnblogs.com/Just--Do--It/p/7326572.html