HYSBZ/BZOJ 3994 约数个数和(莫比乌斯反演)

题意

给你n和m求
∑ i = 1 n ∑ j = 1 m d ( i j ) \sum_{i=1}^n\sum_{j=1}^md(ij) i=1nj=1md(ij)
其中 d ( x ) d(x) d(x)表示 x x x约数的个数

思路

这道题其实有一个很神奇的结论
∑ i = 1 n ∑ j = 1 m d ( i j ) = ∑ g c d ( i , j ) = = 1 ⌊ n i ⌋ ⌊ m j ⌋ \sum_{i=1}^n\sum_{j=1}^md(ij)=\sum_{gcd(i,j)==1}\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{j} \right \rfloor i=1nj=1md(ij)=gcd(i,j)==1injm
∑ i = 1 n ∑ j = 1 m ⌊ n i ⌋ ⌊ m j ⌋ [ g c d ( i , j ) = = 1 ] \sum_{i=1}^n\sum_{j=1}^m\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{j} \right \rfloor[gcd(i,j)==1] i=1nj=1minjm[gcd(i,j)==1]
那么有了这个式子之后我们就可做了,已知
∑ d ∣ n μ ( d ) = [ n = = 1 ] \sum_{d|n}\mu(d)=[n==1] dnμ(d)=[n==1]
n = g c d ( i , j ) n=gcd(i,j) n=gcd(i,j)带入上式有
∑ i = 1 n ∑ j = 1 m ⌊ n i ⌋ ⌊ m j ⌋ ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{i=1}^n\sum_{j=1}^m\left \lfloor \frac{n}{i} \right \rfloor\left \lfloor \frac{m}{j} \right \rfloor\sum_{d|gcd(i,j)}\mu(d) i=1nj=1minjmdgcd(i,j)μ(d)
我们改变枚举变量,令 N = m i n ( n , m ) N=min(n,m) N=min(n,m),原来枚举的是 g c d ( i , j ) gcd(i,j) gcd(i,j)的因子,而 g c d ( i , j ) gcd(i,j) gcd(i,j)的范围为 1 ∼ N 1\sim N 1N,相当于枚举 1 ∼ N 1\sim N 1N中每个数的因子,这和枚举 1 ∼ N 1\sim N 1N中每个数的倍数是等价的,故有
= ∑ d = 1 N μ ( d ) ∑ i = 1 n d ⌊ n i d ⌋ ∑ j = 1 m d ⌊ m j d ⌋ =\sum_{d=1}^N\mu(d) \sum_{i=1}^{\frac{n}{d} }\left \lfloor \frac{n}{id} \right \rfloor\sum_{j=1}^{ \frac{m}{d}}\left \lfloor \frac{m}{jd} \right \rfloor =d=1Nμ(d)i=1dnidnj=1dmjdm
我们令 f ( n ) = ∑ i = 1 n ⌊ n i ⌋ f(n)=\sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor f(n)=i=1nin
可以看出上式是枚举 1 ∼ n 1\sim n 1n中每个数的倍数有多少个,这个和每个数的约数有多少个是等价的,所以 f ( n ) f(n) f(n)枚举的就是 1 ∼ N 1\sim N 1N中每个数的约数和,那么我们只要筛出每个数的约数个数再做一个前缀和就可以得到 f ( n ) f(n) f(n)
那么原式有
= ∑ d = 1 N μ ( d ) f ( ⌊ n d ⌋ ) f ( ⌊ m d ⌋ ) =\sum_{d=1}^N\mu(d) f(\left \lfloor \frac{n}{d} \right \rfloor)f(\left \lfloor \frac{m}{d} \right \rfloor) =d=1Nμ(d)f(dn)f(dm)
就可以在 O ( n + m ) O(\sqrt{n}+\sqrt{m}) O(n +m )的复杂度下求解了

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int d[N];
int prime[N];
int vis[N];
int num[N];
int mu[N];
int cnt;
void init()
{
    cnt=0;
    d[1]=1;
    mu[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
        {
            prime[cnt++]=i;
            d[i]=2;
            num[i]=1;
            mu[i]=-1;
        }
        for(int j=0;prime[j]*i<N&&j<cnt;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]!=0)
            {
                d[i*prime[j]]=d[i]*2;
                num[i*prime[j]]=1;
                mu[i*prime[j]]=-mu[i];
            }
            else
            {
                d[i*prime[j]]=d[i]/(num[i]+1)*(num[i]+2);
                num[i*prime[j]]=num[i]+1;
                mu[i*prime[j]]=0;
                break;
            }
        }
    }
    for(int i=1;i<N;i++)
        {
            d[i]=d[i-1]+d[i];
            mu[i]=mu[i-1]+mu[i];
        }
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        long long sum=0;
        int minn=min(n,m);
        for(int i=1,last;i<=minn;i=last+1)
        {
            last=min(n/(n/i),m/(m/i));
            sum+=1ll*(mu[last]-mu[i-1])*d[n/i]*d[m/i];
        }
        printf("%lld\n",sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值