51Nod1220 约数之和 【杜教筛】

本文深入解析了一个数学算法竞赛题目,探讨了如何求解Σi=1nΣj=1nσ1(i∗j)的值,利用质因数分解和数论函数的性质,通过杜教筛法和线性筛法优化计算过程,最终给出了一种高效的时间复杂度为O(n^(2/3))的解题方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

∑i=1n∑j=1nσ1(i∗j)\sum_{i=1}^n\sum_{j=1}^n\sigma_1(i*j)i=1nj=1nσ1(ij)
n≤109,mod 109+7n\le10^9,mod ~10^9+7n109mod 109+7

题目分析:

有个结论:σk(i∗j)=∑x∣i∑y∣j[(x,y)==1] xk∗jk/yk\sigma_k(i*j)=\sum_{x|i}\sum_{y|j}[(x,y)==1]~x^k*j^k/y^kσk(ij)=xiyj[(x,y)==1] xkjk/yk
σk(n)\sigma_k(n)σk(n)nnn的约数的kkk次幂之和,质因数分解nnnn=p1a1p2a2...ptatn=p_1^{a_1}p_2^{a_2}...p_t^{a_t}n=p1a1p2a2...ptat
σk(n)=(1+p11k+p12k+⋯+p1a1k)           ∗(1+p21k+p22k+⋯+p2a2k)            …          ∗(1+pt1k+pt2k+⋯+ptatk)\sigma_k(n)=(1+p_1^{1k}+p_1^{2k}+\dots+p_1^{a_1k})\\~~~~~~~~~~~*(1+p_2^{1k}+p_2^{2k}+\dots+p_2^{a_2k})\\~~~~~~~~~~~~\dots\\~~~~~~~~~~*(1+p_t^{1k}+p_t^{2k}+\dots+p_t^{a_tk})σk(n)=(1+p11k+p12k++p1a1k)           (1+p21k+p22k++p2a2k)                      (1+pt1k+pt2k++ptatk)
考虑一个质因子ppp,它对σk(n)\sigma_k(n)σk(n)的贡献为1+pk+p2k+...+pak1+p^k+p^{2k}+...+p^{ak}1+pk+p2k+...+pak
那么看σk(i∗j)\sigma_k(i*j)σk(ij),考虑它们的公共质因子ppp,在i中有a次,在j中有b次,造成的贡献就可以表示为∑x=0a∑y=0b[(px,py)==1] pxk∗pbk/pyk\sum_{x=0}^a\sum_{y=0}^b[(p^x,p^y)==1]~p^{xk}*p^{bk}/p^{yk}x=0ay=0b[(px,py)==1] pxkpbk/pyk
根据乘法原理,有σk(i∗j)=∑x1=0a1∑y1=0b1[(px1,py1)==1] px1k∗pb1k/py1k               ∗∑x2=0a2∑y2=0b2[(px2,py2)==1] px2k∗pb2k/py2k…               ∗∑xt=0at∑yt=0bt[(pxt,pyt)==1] pxtk∗pbtk/pytk\sigma_k(i*j)=\sum_{x_1=0}^{a_1}\sum_{y_1=0}^{b_1}[(p^{x_1},p^{y_1})==1]~p^{x_1k}*p^{b_1k}/p^{y_1k}\\~~~~~~~~~~~~~~~* \sum_{x_2=0}^{a_2}\sum_{y_2=0}^{b_2}[(p^{x_2},p^{y_2})==1]~p^{x_2k}*p^{b_2k}/p^{y_2k}\\\dots\\ ~~~~~~~~~~~~~~~*\sum_{x_t=0}^{a_t}\sum_{y_t=0}^{b_t}[(p^{x_t},p^{y_t})==1]~p^{x_tk}*p^{b_tk}/p^{y_tk}σk(ij)=x1=0a1y1=0b1[(px1,py1)==1] px1kpb1k/py1k               x2=0a2y2=0b2[(px2,py2)==1] px2kpb2k/py2k               xt=0atyt=0bt[(pxt,pyt)==1] pxtkpbtk/pytk
只有当所有(px,py)==1(p^x,p^y)==1(px,py)==1都满足时才会对答案造成贡献,所以可变形为:
σk(i∗j)=∑x∣i∑y∣j[(x,y)==1] xk∗jk/yk\sigma_k(i*j)=\sum_{x|i}\sum_{y|j}[(x,y)==1]~x^k*j^k/y^kσk(ij)=xiyj[(x,y)==1] xkjk/yk

ok,正文开始

∑i=1n∑j=1nσ1(i∗j)=∑i=1n∑j=1n∑x∣i∑y∣j[(x,y)==1] x∗j/y=∑k=1nμ(k)∑x=1nk⌊nkx⌋∑y=1nk∑j=1nkyxk∗kyj/ky=∑k=1nμ(k)k∑x=1nk⌊nkx⌋x∑j=1nkj⌊nkj⌋=∑k=1nμ(k)k(∑x=1nk⌊nkx⌋x)2=∑k=1nμ(k)k(∑i=1nkσ1(i))2\large\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^n\sigma_1(i*j)\\ &=\sum_{i=1}^n\sum_{j=1}^n\sum_{x|i}\sum_{y|j}[(x,y)==1]~x*j/y\\ &=\sum_{k=1}^n\mu(k)\sum_{x=1}^{\frac nk}\lfloor\frac n{kx}\rfloor\sum_{y=1}^{\frac nk}\sum_{j=1}^{\frac n{ky}}xk*kyj/ky\\ &=\sum_{k=1}^n\mu(k)k\sum_{x=1}^{\frac nk}\lfloor\frac n{kx}\rfloor x\sum_{j=1}^{\frac nk}j\lfloor\frac n{kj}\rfloor\\ &=\sum_{k=1}^n\mu(k)k\left(\sum_{x=1}^{\frac nk}\lfloor\frac n{kx}\rfloor x\right)^2\\ &=\sum_{k=1}^n\mu(k)k\left(\sum_{i=1}^{\frac nk}\sigma_1(i)\right)^2 \end{aligned}i=1nj=1nσ1(ij)=i=1nj=1nxiyj[(x,y)==1] xj/y=k=1nμ(k)x=1knkxny=1knj=1kynxkkyj/ky=k=1nμ(k)kx=1knkxnxj=1knjkjn=k=1nμ(k)kx=1knkxnx2=k=1nμ(k)ki=1knσ1(i)2

线性筛10610^6106μ(k)k\mu(k)kμ(k)k标准的杜教筛,σ1(n)的前缀和可以n\sigma_1(n)的前缀和可以\sqrt nσ1(n)n分块暴算
时间复杂度O(n23)O(n^{\frac 23})O(n32)

  • 其实有种更简单清晰的推法,第一步先不要把x,y提到最前面
    =∑i=1n∑j=1n∑x∣i∑y∣j[(x,y)==1] x∗j/y=∑k=1nμ(k)∑i=1nk∑j=1nk∑x∣i∑y∣ixk∗jk/yk=∑k=1nμ(k)k∑i=1nk∑j=1nk∑x∣ix∑y∣jjy=∑k=1nμ(k)k∑i=1nk∑j=1nkσ1(i)∗σ1(j)=∑k=1nμ(k)k(∑i=1nkσ1(i))2\begin{aligned} &=\sum_{i=1}^n\sum_{j=1}^n\sum_{x|i}\sum_{y|j}[(x,y)==1]~x*j/y\\ &=\sum_{k=1}^n\mu(k)\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac nk}\sum_{x|i}\sum_{y|i}xk*jk/yk\\ &=\sum_{k=1}^n\mu(k)k\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac nk}\sum_{x|i}x\sum_{y|j}{\frac jy}\\ &=\sum_{k=1}^n\mu(k)k\sum_{i=1}^{\frac nk}\sum_{j=1}^{\frac nk}\sigma_1(i)*\sigma_1(j)\\ &=\sum_{k=1}^n\mu(k)k\left(\sum_{i=1}^{\frac nk}\sigma_1(i)\right)^2 \end{aligned}=i=1nj=1nxiyj[(x,y)==1] xj/y=k=1nμ(k)i=1knj=1knxiyixkjk/yk=k=1nμ(k)ki=1knj=1knxixyjyj=k=1nμ(k)ki=1knj=1knσ1(i)σ1(j)=k=1nμ(k)ki=1knσ1(i)2
#include<cstdio>
#include<map>
using namespace std;
const int N = 1000000, mod = 1e9+7, inv2 = 5e8+4;
int p[N/10],sm[N+5],sd[N+5],mp[N+5];
bool v[N+5];
map<int,int>F;
void Prime()
{
	sm[1]=sd[1]=1;int cnt=0;
	for(int i=2;i<=N;i++)
	{
		if(!v[i]) p[++cnt]=i,sm[i]=-i,sd[i]=mp[i]=i+1;
		for(int j=1,k;j<=cnt&&(k=p[j]*i)<=N;j++)
		{
			v[k]=1;
			if(i%p[j]==0) {sm[k]=0,mp[k]=mp[i]*p[j]+1,sd[k]=sd[i]/mp[i]*mp[k];break;}
			sm[k]=sm[i]*sm[p[j]];
			sd[k]=sd[i]*sd[p[j]];
			mp[k]=p[j]+1;
		}
	}
	for(int i=1;i<=N;i++) sm[i]=(sm[i]+sm[i-1])%mod,sd[i]=(sd[i]+sd[i-1])%mod;
}
int Summu(int n)
{
	if(n<=N) return sm[n];
	if(F.count(n)) return F[n];
	int ret=1;
	for(int i=2,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		ret=(ret-1ll*(i+j)*(j-i+1)%mod*inv2%mod*Summu(n/i))%mod;
	}
	return F[n]=ret;
}
int calc(int n)
{
	if(n<=N) return sd[n];
	int ret=0;
	for(int i=1,j;i<=n;i=j+1)
	{
		j=n/(n/i);
		ret=(ret-1ll*(i+j)*(j-i+1)%mod*inv2%mod*(n/i))%mod;
	}
	return ret;
}
int main()
{
	Prime();
	int n,ans=0;
	scanf("%d",&n);
	for(int i=1,j,tmp,pre=0,tt;i<=n;i=j+1)
	{
		j=n/(n/i);
		ans=(ans+1ll*((tmp=Summu(j))-pre)*(tt=calc(n/i))%mod*tt)%mod;
		pre=tmp;
	}
	printf("%d",(ans+mod)%mod);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值