Luogu P3327 [SDOI2015]约数个数和/BZOJ 4176 Lucas的数论

题目大意

共有 T T T 组数据,给定 n , m n,m n,m,设 d ( x ) = ∑ i ∣ x 1 d(x)=\sum_{i|x}1 d(x)=ix1,求 ∑ i = 1 n ∑ j = 1 m d ( i j ) \sum_{i=1}^n\sum_{j=1}^md(ij) i=1nj=1md(ij)

数据范围  T = 1 ( 不输入 ) , 1 ⩽ n = m ( 不输入 ) ⩽ 1 0 9 T=1(不输入),1\leqslant n=m(不输入)\leqslant 10^9 T=1(不输入),1n=m(不输入)109 或  1 ⩽ n , m , T ⩽ 50000 1\leqslant n,m,T\leqslant 50000 1n,m,T50000

题解

由于两道题目有一定不同,这里考虑最大范围,但不管如何,先来证证证……


结论  d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ ( x , y ) = 1 ] \begin{aligned}d(ij)=\sum_{x|i}^{^{^{^{^{}}}}}\sum_{y|j}[(x,y)=1]\end{aligned} d(ij)=xiyj[(x,y)=1]

证明 对于 i j ij ij,其唯一分解为 i j = ∏ d = 1   n p d k d \begin{aligned}ij=\prod\limits_{d=1}^{\ n^{^{^{^{}}}}}p_d^{k_d}\end{aligned} ij=d=1 npdkd,则 d ( i j ) = ∏ d = 1   n ( k d + 1 ) \begin{aligned}d(ij)=\prod\limits_{d=1}^{\ n^{^{^{^{}}}}}(k_d+1)\end{aligned} d(ij)=d=1 n(kd+1)
考虑一个质数 p p p d ( i j ) d(ij) d(ij) 的贡献,设 i = p k ∗ t 1 , j = p q ∗ t 2 i=p^k*t_1,j=p^q*t_2 i=pkt1,j=pqt2,则对 d ( i j ) d(ij) d(ij) 的贡献为 k + q + 1 k+q+1 k+q+1
再来看一个引理。


引理 设 i = p k ∗ t 1 , j = p q ∗ t 2 i=p^k*t_1,j=p^q*t_2 i=pkt1,j=pqt2,则有: ∑ x = 0 k ∑ y = 0 q [ ( p x , p y ) = 1 ] = q + k + 1 \begin{aligned}\sum\limits_{x=0}^k\sum\limits_{y=0}^q[(p^x,p^y)=1]=q+k+1\end{aligned} x=0ky=0q[(px,py)=1]=q+k+1
证明 可以发现,当且仅当 x y = 0 xy=0 xy=0 时, ( p x , p y ) = 1 (p^x,p^y)=1 (px,py)=1,故成立。


因此对质数 p p p d ( i , j ) d(i,j) d(i,j) 的贡献的贡献为 ∑ x = 0 k ∑ y = 0 q [ ( p x , p y ) = 1 ] \begin{aligned}\sum\limits_{x=0}^k\sum\limits_{y=0}^q[(p^x,p^y)=1]\end{aligned} x=0ky=0q[(px,py)=1]
接下来设 i = ∏ i = 1 n p i k i , j = ∏ i = 1 n p i q i \begin{aligned}i=\prod_{i=1}^n\limits p_i^{k_i},j=\prod_{i=1}^{n^{^{^{^{}}}}}\limits p_i^{q_i}\end{aligned} i=i=1npiki,j=i=1npiqi,考虑下面逐一每一个质数 p i p_i pi,根据乘法原理得:
d ( i j ) = ∑ x 1 = 0 k 1 ∑ y 1 = 0 q 1 [ ( p 1 x 1 , p 1 y 1 ) = 1 ] ∑ x 2 = 0 k 2 ∑ y 2 = 0 q 2 [ ( p 2 x 2 , p 2 y 2 ) = 1 ] . . . ∑ x n = 0 k n ∑ y n = 0 q n [ ( p n x n , p n y n ) = 1 ] d(ij)=\sum_{x_1=0}^{k_1}\sum_{y_1=0}^{q_1}[(p_1^{x_1},p_1^{y_1})=1]\sum_{x_2=0}^{k_2}\sum_{y_2=0}^{q_2}[(p_2^{x_2},p_2^{y_2})=1]...\sum_{x_n=0}^{k_n}\sum_{y_n=0}^{q_n}[(p_n^{x_n},p_n^{y_n})=1] d(ij)=x1=0k1y1=0q1[(p1x1,p1y1)=1]x2=0k2y2=0q2[(p2x2,p2y2)=1]...xn=0knyn=0qn[(pnxn,pnyn)=1]

d ( i j ) = ∑ d = 1 n ∑ x d = 0 k d ∑ y d = 0 q d [ ( p d x d , p d y d ) = 1 ] d(ij)=\sum_{d=1}^n\sum_{x_d=0}^{k_d}\sum_{y_d=0}^{q_d}[(p_d^{x_d},p_d^{y_d})=1] d(ij)=d=1nxd=0kdyd=0qd[(pdxd,pdyd)=1]

可以发现,对于下面这个式子
∑ x ∣ i ∑ y ∣ j [ ( x , y ) = 1 ] \sum_{x|i}\sum_{y|j}[(x,y)=1] xiyj[(x,y)=1]

如果 x x x y y y 含有的不相同的质因子,它们不会对答案造成任何影响,因而相当于仅仅枚举了两者相同的质因子的个数,因此,我们有
∑ x ∣ i ∑ y ∣ j [ ( x , y ) = 1 ] = ∑ d = 1 n ∑ x d = 0 k d ∑ y d = 0 q d [ ( p d x d , p d y d ) = 1 ] \sum_{x|i}\sum_{y|j}[(x,y)=1]=\sum_{d=1}^n\sum_{x_d=0}^{k_d}\sum_{y_d=0}^{q_d}[(p_d^{x_d},p_d^{y_d})=1] xiyj[(x,y)=1]=d=1nxd=0kdyd=0qd[(pdxd,pdyd)=1]

因而 d ( i j ) = ∑ x ∣ i ∑ y ∣ j [ ( x , y ) = 1 ] d(ij)=\sum_{x|i}\sum_{y|j}[(x,y)=1] d(ij)=xiyj[(x,y)=1]得证。


代入题目,得:
∑ i = 1 n ∑ j = 1 m d ( i j ) = ∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j [ ( x , y ) = 1 ] = ∑ i = 1 n ∑ j = 1 m ∑ x ∣ i ∑ y ∣ j ∑ d ∣ ( x , y ) μ ( d ) \begin{aligned} \sum_{i=1}^n\sum_{j=1}^md(ij)&=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}[(x,y)=1]\\ &=\sum_{i=1}^n\sum_{j=1}^m\sum_{x|i}\sum_{y|j}\sum_{d|(x,y)}\mu(d)\\ \end{aligned} i=1nj=1md(ij)=i=1nj=1mxiyj[(x,y)=1]=i=1nj=1mxiyjd(x,y)μ(d)

各回各家,各找各妈,好好理解,实在不行用C++证明法。
原式 = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ( ∑ d ∣ x x ⩽ n ∑ x ∣ i i ⩽ n 1 ) ( ∑ d ∣ y y ⩽ m ∑ y ∣ j i ⩽ m 1 ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ( ∑ d ∣ x x ⩽ n ⌊ n x ⌋ ) ( ∑ d ∣ y y ⩽ m ⌊ m y ⌋ ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ( ∑ x = 1 ⌊ n d ⌋ ⌊ n d x ⌋ ) ( ∑ y = 1 ⌊ m d ⌋ ⌊ m d y ⌋ ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ( ∑ x = 1 ⌊ n d ⌋ ⌊ ⌊ n d ⌋ x ⌋ ) ( ∑ y = 1 ⌊ m d ⌋ ⌊ ⌊ m d ⌋ y ⌋ ) \begin{aligned} 原式&=\sum_{d=1}^{\min(n,m)}\mu(d)\left(\sum_{d|x}^{x\leqslant n}\sum_{x|i}^{i\leqslant n}1\right)\left(\sum_{d|y}^{y\leqslant m}\sum_{y|j}^{i\leqslant m}1\right)\\ &=\sum_{d=1}^{\min(n,m)}\mu(d)\left(\sum_{d|x}^{x\leqslant n}\left\lfloor\frac nx\right\rfloor\right)\left(\sum_{d|y}^{y\leqslant m}\left\lfloor\frac my\right\rfloor\right)\\ &=\sum_{d=1}^{\min(n,m)}\mu(d)\left(\sum_{x=1}^{\large\lfloor\frac nd\rfloor}\left\lfloor\frac n{dx}\right\rfloor\right)\left(\sum_{y=1}^{\left\lfloor\frac md\right\rfloor}\left\lfloor\frac m{dy}\right\rfloor\right)\\ &=\sum_{d=1}^{\min(n,m)}\mu(d)\left(\sum_{x=1}^{\left\lfloor\frac nd\right\rfloor}\left\lfloor\frac {\left\lfloor\frac nd\right\rfloor}{x}\right\rfloor\right)\left(\sum_{y=1}^{\left\lfloor\frac md\right\rfloor}\left\lfloor\frac {\left\lfloor\frac md\right\rfloor}{y}\right\rfloor\right) \end{aligned} 原式=d=1min(n,m)μ(d) dxxnxiin1 dyymyjim1 =d=1min(n,m)μ(d) dxxnxn dyymym =d=1min(n,m)μ(d) x=1dndxn y=1dmdym =d=1min(n,m)μ(d) x=1dnxdn y=1dmydm

Luogu

Θ ( n n ) \Theta(n\sqrt n) Θ(nn )预处理出 f ( x ) = ∑ i = 1 n ⌊ n i ⌋ f(x)=\sum_{i=1}^n\lfloor\frac ni\rfloor f(x)=i=1nin,线筛出 μ \mu μ 即可,胜在代码短。

#include<bits/stdc++.h>
using namespace std;
#define maxn 50010
long long Sqrt[maxn];
long long s_sqrt(long long n){
    long long ans=0;
    for(long long l=1,r;l<=n;l=r+1){
        r=min(n/(n/l),n);
        ans+=(r-l+1)*(n/l);
    } return ans;
}
long long p[maxn],vis[maxn],mu[maxn],smu[maxn],calced=-1;
void euler(long long n=maxn-10){
    calced=n;
    long long tot=0;
    memset(vis,0,sizeof vis);
    mu[1]=1;
    for(long long i=2;i<=n;i++){
        if(vis[i]==0) p[++tot]=i,mu[i]=-1;
        for(long long j=1;j<=tot&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
                mu[p[j]*i]=0;
                break;
            } else mu[p[j]*i]=-mu[i];
        }
    } smu[0]=0;
    for(long long i=1;i<=n;i++)
        smu[i]=smu[i-1]+mu[i];
}
long long T,n,m;
signed main(void){
    euler(50000);
    for(int i=1;i<=maxn;i++)
        Sqrt[i]=s_sqrt(i);
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        long long ans=0;
        for(long long l=1,r;l<=min(n,m);l=r+1){
            r=min(min(n/(n/l),m/(m/l)),min(n,m));
            ans+=(smu[r]-smu[l-1])*Sqrt[n/l]*Sqrt[m/l];
        } printf("%lld\n",ans);
    }
    return 0;
}

还能更好吗?这个时间几乎时卡着过去的。当然可以。有
∑ i = 1 n ⌊ n i ⌋ = ∑ i = 1 n ∑ i ∣ k k ⩽ n 1 = ∑ k = 1 n ∑ i ∣ k 1 = ∑ k = 1 n d ( k ) \begin{aligned}\sum_{i=1}^n\left\lfloor\frac ni\right\rfloor &=\sum_{i=1}^n\sum_{i|k}^{k\leqslant n}1\\ &=\sum_{k=1}^n\sum_{i|k}1\\ &=\sum_{k=1}^nd(k)\\ \end{aligned} i=1nin=i=1nikkn1=k=1nik1=k=1nd(k)

其中 d d d 是约数和函数。 d d d 可以在 Θ ( n ) \Theta(n) Θ(n) 的时间内筛出来,时间复杂度为 Θ ( n + T n ) \Theta(n+T\sqrt n) Θ(n+Tn )

#include<bits/stdc++.h>
using namespace std;
#define maxn 50010
long long low[maxn],d[maxn],sd[maxn];
long long p[maxn],vis[maxn],mu[maxn],smu[maxn],calced=-1;
void euler(long long n=maxn-10){
    calced=n;
    long long tot=0;
    memset(vis,0,sizeof vis);
    mu[1]=1;smu[1]=1;d[1]=1;low[1]=1;sd[1]=1;
    for(long long i=2;i<=n;i++){
        if(vis[i]==0) low[i]=p[++tot]=i,mu[i]=-1,d[i]=2;
        for(long long j=1;j<=tot&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
            	low[i*p[j]]=low[i]*p[j];
                if(low[i]==i) d[i*p[j]]=d[i]+1;
                else d[i*p[j]]=d[i/low[i]]*d[low[i]*p[j]];
                mu[p[j]*i]=0;
                break;
            } mu[p[j]*i]=-mu[i];
			low[i*p[j]]=p[j];
            d[i*p[j]]=d[i]*d[p[j]];
        } smu[i]=smu[i-1]+mu[i];
        sd[i]=sd[i-1]+d[i];
    }
}
long long T,n,m;
signed main(void){
    euler();
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        long long ans=0;
        for(long long l=1,r;l<=min(n,m);l=r+1){
            r=min(min(n/(n/l),m/(m/l)),min(n,m));
            ans+=(smu[r]-smu[l-1])*sd[n/l]*sd[m/l];
        } printf("%lld\n",ans);
    }
    return 0;
}
BZOJ

%   BZOJ 要求设计非线性算法,因而 μ \mu μ 的前缀和需要用杜教筛求出,由于 n = m n=m n=m,有:
∑ d = 1 min ⁡ ( n , m ) μ ( d ) ( ∑ x = 1 ⌊ n d ⌋ ⌊ ⌊ n d ⌋ x ⌋ ) ( ∑ y = 1 ⌊ m d ⌋ ⌊ ⌊ m d ⌋ y ⌋ ) = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ( ∑ x = 1 ⌊ n d ⌋ ⌊ ⌊ n d ⌋ x ⌋ ) 2 \sum_{d=1}^{\min(n,m)}\mu(d)\Bigg(\sum_{x=1}^{\large\lfloor\frac nd\rfloor}\Big\lfloor\frac {\large\lfloor\frac nd\rfloor}{x}\Big\rfloor\Bigg)\Bigg(\sum_{y=1}^{\large\lfloor\frac md\rfloor}\Big\lfloor\frac {\large\lfloor\frac md\rfloor}{y}\Big\rfloor\Bigg)=\sum_{d=1}^{\min(n,m)}\mu(d)\Bigg(\sum_{x=1}^{\large\lfloor\frac nd\rfloor}\Big\lfloor\frac {\large\lfloor\frac nd\rfloor}{x}\Big\rfloor\Bigg)^2 d=1min(n,m)μ(d)(x=1dnxdn)(y=1dmydm)=d=1min(n,m)μ(d)(x=1dnxdn)2  这样求第二个sigma的时间复杂度为: T ( n ) = Θ ( ⌊ n d ⌋ ) T(n)=\Theta\left(\sqrt{\left\lfloor\frac{n}{d}\right\rfloor}\right) T(n)=Θ(dn )  因而计算这个sigma的总时间复杂度为
T ( n ) = Θ ( n 1 ) + Θ ( n 2 ) + . . . + Θ ( n n ) = Θ ( n 3 4 ) T(n)=\Theta\left(\sqrt\frac n1\right)+\Theta\left(\sqrt\frac n2\right)+...+\Theta\left(\sqrt\frac n{\sqrt n}\right)=\Theta\left(n^{\normalsize\frac34}\right) T(n)=Θ(1n )+Θ(2n )+...+Θ(n n )=Θ(n43)  因而总时间复杂度为 T ( n ) = Θ ( n 2 3 + n 3 4 ) T(n)=\Theta\left(n^{\normalsize\frac 23}+n^{\normalsize \frac 34}\right) T(n)=Θ(n32+n43)

#include<bits/stdc++.h>
using namespace std;
#define maxn 2000000
#define mod 1000000007
long long s_sqrt(long long n){
    long long ans=0;
    for(long long l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans=(ans+(r-l+1)*(n/l))%mod;
    } return ans*ans%mod;
}
long long p[maxn],vis[maxn],mu[maxn],smu[maxn],calced=-1;
void euler(long long n=maxn-10){
    calced=n;
    long long tot=0;
    memset(vis,0,sizeof vis);
    mu[1]=1;smu[1]=1;
    for(long long i=2;i<=n;i++){
        if(vis[i]==0) p[++tot]=i,mu[i]=-1;
        for(long long j=1;j<=tot&&i*p[j]<=n;j++){
            vis[i*p[j]]=1;
            if(i%p[j]==0){
                mu[p[j]*i]=0;
                break;
            } else mu[p[j]*i]=mod-mu[i];
        } smu[i]=(smu[i-1]+mu[i]+mod)%mod;
    }
}
map<long long,long long> Smu;
long long Sum_mu(long long n){
    if(n<=calced) return smu[n];
    map<long long,long long>::iterator it=Smu.find(n);
    if(it!=Smu.end()) return it->second;
    long long ans=1;
    for(long long l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        ans=(ans-Sum_mu(n/l)*(r-l+1)%mod+mod)%mod;
    } return Smu[n]=ans%mod;
}
long long n;
signed main(void){
    euler();
    scanf("%lld",&n);
    long long ans=0;
    for(long long l=1,r;l<=n;l=r+1){
        r=n/(n/l);
        ans=(ans+(Sum_mu(r)-Sum_mu(l-1)+mod)%mod*s_sqrt(n/l)%mod)%mod;
    } printf("%lld\n",ans%mod);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值