zoukankan      html  css  js  c++  java
  • 积性函数求和

    (全是套路.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;
    }
    
  • 相关阅读:
    Git push 常见用法
    Git commit 常见用法
    Git add 常见用法
    Git-仓库
    Git clone 常见用法
    Git-简介
    ZOJ Problem Set
    ZOJ Problem Set
    ZOJ Problem Set
    ZOJ Problem Set
  • 原文地址:https://www.cnblogs.com/Camp-Nou/p/13546292.html
Copyright © 2011-2022 走看看