积性函数求和

(全是套路.jpg)

题目

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

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

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

\[\sum _{i=1}^nf(i)\\=\sum _{i=1}^n\sum_{d|i} [d\bot \frac id]d\\=\sum _{d=1}^nd\sum_{d|i} [d\bot \frac id]\\=\sum _{d=1}^nd\sum_{i=1}^{\lfloor\frac nd\rfloor} [d\bot i]\\=\sum _{d=1}^nd\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{t|d,t|i}\mu(t)\\=\sum _{d=1}^nd\sum_{t|d}\mu(t)\lfloor\frac n{dt}\rfloor\\=\sum_{T=1}^n\lfloor\frac nT\rfloor\sum _{t^2|T}\mu(t)\frac Tt\\=\sum_{t=1}^{\sqrt n}\frac {\mu(t)}t\sum_{t^2|T}T\lfloor\frac nT\rfloor\\=\sum_{t=1}^{\sqrt n}\frac {\mu(t)}t\sum_{T=1}^{\lfloor\frac n{t^2}\rfloor}Tt^2\lfloor\frac n{Tt^2}\rfloor\\=\sum_{t=1}^{\sqrt n}\mu(t)t\sum_{T=1}^{\lfloor\frac n{t^2}\rfloor}T\lfloor\frac{\lfloor\frac n{t^2}\rfloor}{T}\rfloor\\ \]

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

\[\sum_{t=1}^\sqrt n \frac{ \sqrt n}t=\sqrt n\sum_{t=1}^\sqrt n \frac{1}t\sim \sqrt n\ln n \]

总复杂度 \(O(\sqrt n\ln n)\)

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

  • 瓶颈在除法分块,把这个除法分块写成比较快的版本,可以快至少一倍;
  • 注意到 \(g(n)=\sum_{T=1}^nT\lfloor\frac n{T}\rfloor=\sum_{i=1}^n\sigma(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\n",s>>1);
	return 0;
}

posted on 2020-08-22 17:02  Dreamunk  阅读(323)  评论(0)    收藏  举报

导航