积性函数求和

(全是套路.jpg)

题目

首先观察这个函数,发现是 (f(n)=prod_j(p_j^{k_j}+1))(设 (n) 质因数分解得到 (prod_jp_j^{k_j})

然后不知怎么想到的,就等价于 (f(n)=sum_{d|n} [dot frac nd]d)

终于有了比较靠谱的形式,然后就开始推:

[sum _{i=1}^nf(i)\=sum _{i=1}^nsum_{d|i} [dot frac id]d\=sum _{d=1}^ndsum_{d|i} [dot frac id]\=sum _{d=1}^ndsum_{i=1}^{lfloorfrac nd floor} [dot i]\=sum _{d=1}^ndsum_{i=1}^{lfloorfrac nd floor}sum_{t|d,t|i}mu(t)\=sum _{d=1}^ndsum_{t|d}mu(t)lfloorfrac n{dt} floor\=sum_{T=1}^nlfloorfrac nT floorsum _{t^2|T}mu(t)frac Tt\=sum_{t=1}^{sqrt n}frac {mu(t)}tsum_{t^2|T}Tlfloorfrac nT floor\=sum_{t=1}^{sqrt n}frac {mu(t)}tsum_{T=1}^{lfloorfrac n{t^2} floor}Tt^2lfloorfrac n{Tt^2} floor\=sum_{t=1}^{sqrt n}mu(t)tsum_{T=1}^{lfloorfrac n{t^2} floor}Tlfloorfrac{lfloorfrac n{t^2} floor}{T} floor\ ]

后面那个求和号显然可以除法分块 (O(frac{sqrt n}t)) 求,则

[sum_{t=1}^sqrt n frac{ sqrt n}t=sqrt nsum_{t=1}^sqrt n frac{1}tsim sqrt nln n ]

总复杂度 (O(sqrt nln n))

然后由于毒瘤出题人把 (n) 出到了 (10^{13}),你发现你被卡常数了 爆筋

  • 瓶颈在除法分块,把这个除法分块写成比较快的版本,可以快至少一倍;
  • 注意到 (g(n)=sum_{T=1}^nTlfloorfrac n{T} floor=sum_{i=1}^nsigma(i)) ,这玩意可以线筛。于是把较小的 (g) 拿去线筛预处理一下,省下的计算也比较可观。

至此大概就可以过了。

#include<cstdio>
typedef long long ll;
const int D=3.2e6;
int M,p[D],np[D],k,mu[D],t0[D],t1[D],s,sig[D];ll n;
inline int G(ll n){
	if(n<D)return sig[n];
	int res=0;ll d,e,i;
	e=n%M;
	for(i=1;i*i<=n;i++){
	  d=e,e=n/(i+1)%M;
	  res=(res+i*d*2)%M;
	  if(n>=i*(i+1))res=(res+(d-e)*(d+e+1)%M*i)%M;
	}
	return res;
}
int main(){
	scanf("%lld%d",&n,&M);
	mu[1]=1,sig[1]=1;
	for(int i=2;i<D;i++){
	  if(!np[i])p[++k]=i,mu[i]=-1,t0[i]=t1[i]=1;
	  if(t1[i]==1)sig[i]=sig[t0[i]]+i;
	  else sig[i]=sig[t1[i]]*sig[i/t1[i]];
	  for(int j=1;j<=k&&i*p[j]<D;j++){
		np[i*p[j]]=1;
		t0[i*p[j]]=i;
		if(i%p[j]){
		  mu[i*p[j]]=-mu[i];
		  t1[i*p[j]]=i;
		}
		else{
		  t1[i*p[j]]=t1[i];
		  break;
		}
	  }
	}
	for(int i=1;i<D;i++)sig[i]=(sig[i]*2+sig[i-1])%M; 
	for(ll i=1;i*i<=n;i++)if(mu[i])
	  s=(s+G(n/(i*i))*mu[i]*i)%M;
	if(s<0)s+=M;
	if(s&1)s+=M; 
	printf("%d
",s>>1);
	return 0;
}
原文地址:https://www.cnblogs.com/Camp-Nou/p/13546292.html