【51nod1575】Gcd and Lcm(积性函数分析)(min_25筛)

传送门


题解:

首先我们知道 g c d gcd gcd l c m lcm lcm都是可以分质因子来算的,感觉可能会和积性函数沾边。

所以很显然地,我们设 f ( n ) = ∑ i = 1 n ∑ j = 1 n l c m ( g c d ( i , n ) , g c d ( j , n ) ) f(n)=\sum\limits_{i=1}^n\sum\limits_{j=1}^nlcm(gcd(i,n),gcd(j,n)) f(n)=i=1nj=1nlcm(gcd(i,n),gcd(j,n))

容易发现 f ( n ) f(n) f(n)是个积性函数,现在需要算前缀和。

于是我们首先考虑质数点值,很显然地,质数点值就是 f ( p ) = 3 ( p 2 − p ) + 1 f(p)=3(p^2-p)+1 f(p)=3(p2p)+1

然后我们考虑 f ( p k ) f(p^k) f(pk)。。。

具体过程我就不放上来了,反正我这种数学弱菜就只能各种暴力展开,各种特判,各种公式套,这里主要就是展开后等比数列求和,我们可以得到:

f ( p k ) = ( 2 k + 1 ) ( p 2 k − p 2 k − 1 ) + p k − 1 f(p^k)=(2k+1)(p^{2k}-p^{2k-1})+p^{k-1} f(pk)=(2k+1)(p2kp2k1)+pk1

直接上min_25筛即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
#define uint unsigned int

using std::cerr;

inline ll S1(ll n){return n*(n+1)>>1;}
inline ll S2(ll n){
	switch(n%6){
		case 1:return (n+1)*(2*n+1)/6*n;
		case 4:return n*(2*n+1)/6*(n+1);
		default:return n*(n+1)/6*(2*n+1);
	}
}

cs int N=1e5+7;

int n,lim,tot,p[N];
uint c1[N],c2[N],s1[N],s2[N],f1[N],f2[N];

inline void init(){
	lim=sqrt(n),tot=0;
	for(int re i=1;i<=lim;++i){
		c1[i]=i-1;c2[i]=n/i-1;
		s1[i]=S1(i)-1;s2[i]=S1(n/i)-1;
		f1[i]=S2(i)-1;f2[i]=S2(n/i)-1;
	}
	for(uint re p=2;p<=lim;++p)if(c1[p]!=c1[p-1]){
		::p[++tot]=p;
		uint p2=p*p,c=c1[p-1],s=s1[p-1],f=f1[p-1];
		for(int re i=1,li=lim/p;i<=li;++i){
			c2[i]-=c2[i*p]-c;
			s2[i]-=(s2[i*p]-s)*p;
			f2[i]-=(f2[i*p]-f)*p2;
		}
		for(int re i=lim/p+1,li=std::min(n/p/p,(uint)lim);i<=li;++i){
			c2[i]-=c1[n/i/p]-c;
			s2[i]-=(s1[n/i/p]-s)*p;
			f2[i]-=(f1[n/i/p]-f)*p2;
		}
		for(int re i=lim,li=p*p;li<=i;--i){
			c1[i]-=c1[i/p]-c;
			s1[i]-=(s1[i/p]-s)*p;
			f1[i]-=(f1[i/p]-f)*p2;
		}
	}
	for(int re i=1;i<=lim;++i){
		f1[i]=3*(f1[i]-s1[i])+c1[i];
		f2[i]=3*(f2[i]-s2[i])+c2[i];
	}
}

inline uint solve(int n,int i){
	uint ans=(n<=lim?f1[n]:f2[::n/n])-f1[p[i-1]];
	for(int re j=i;j<=tot;++j){
		if(p[j]*p[j]>n)break;
		uint p1=p[j]*(p[j]-1),p2=1,sp=p[j]*p[j];
		for(ll now=p[j],t=1;now<=n;now*=p[j],++t){
			if(now*p[j]<=n)ans+=((2*t+1)*p1+p2)*solve(n/now,j+1);
			if(t>1)ans+=(2*t+1)*p1+p2;
			p1*=sp,p2*=p[j];
		}
	}
	return ans;
}

signed main(){
	int T;std::cin>>T;
	while(T--){
		std::cin>>n;init();
		std::cout<<solve(n,1)+1<<"\n";
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值