【LuoguP3327】约数个数和

题目链接

题目描述

d ( x ) x N , M , i = 1 N j = 1 M d ( i j )
d ( x ) x

题解

首先要知道如下结论:
d ( i j ) = k | i i l | j j g c d ( k , l ) = 1

然后开始随便推式子:
原式化为

i = 1 N j = 1 M k | i i l | j j g c d ( k , l ) = 1

因为我们有 d | n μ ( d ) = 1 ( n = 1 )

i = 1 N j = 1 M k | i i l | j j d | g c d ( k , l ) μ ( d )

对于每一个 k , l 会被算到 N / k M / l (可去掉最外层两个sigma)
k N l M N k M l d | g c d ( k , l ) μ ( d )

枚举d

d = 1 m i n ( N , M ) μ ( d ) k = 1 N d l = 1 M d N k d M l d

d = 1 m i n ( N , M ) μ ( d ) k = 1 N d N k d l = 1 M d M l d

g ( x ) = i = 1 n n i

d = 1 m i n ( N , M ) μ ( d ) g ( N d ) g ( M d )

筛g不能想当然地递推,会WA,而要用筛的方法
其实简单想想就会发现g(n)是n内所有数的约数个数的和,可以分别筛出每个数的约数个数,然后再求前缀和。

约数个数是个积性函数,可以筛。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<queue>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
int n,m;
const int N=5e4+100;
typedef long long ll;
int cnt=0;int pri[N];
int c[N];//某数的质因子的次数中的最小的次数
int mu[N];int g[N];bool vis[N];
inline void prepare()
{
    mu[1]=1;vis[1]=1;g[1]=c[1]=1;
    for(register int i=2;i<N;i++){
        if(!vis[i]) {mu[i]=-1;pri[++cnt]=i;g[i]=2;c[i]=1;}
        for(register int j=1;j<=cnt&&(1ll*pri[j]*i<N);j++)
        {
            register int x=pri[j]*i;
            vis[x]=1;
            if(i%pri[j]) {
                mu[x]=-mu[i];
                g[x]=g[i]*g[pri[j]];c[x]=1;
            }
            else {
                g[x]=g[i]/(c[i]+1)*(c[i]+2);
                c[x]=c[i]+1;mu[x]=0;
                break;
            }
        }
    }
    for(register int i=1;i<N;i++) mu[i]+=mu[i-1];
    for(register int i=2;i<N;i++) g[i]+=g[i-1];
}

int main()
{
    int T=read();
    prepare();
    while(T--)
    {
        n=read();m=read();if(n>m) swap(n,m);
        register int l=0,r=0;register ll ans=0;
        for(l=1;l<=n;l=r+1)
        {
            r=min(n/(n/l),m/(m/l));if(r>n) r=n;
            ans+=1ll*(mu[r]-mu[l-1])*g[n/l]*g[m/l];
        }
        printf("%lld\n",ans);
    }
}

猜你喜欢

转载自blog.csdn.net/element_hero/article/details/79809601