1. 程式人生 > >[51NOD1244]莫比烏斯函式之和

[51NOD1244]莫比烏斯函式之和

題目大意

給定ab,試求出

i=abμ(i)

2ab1010

題目分析

杜教篩裸題。
S(n)=ni=1μ(i)。用恆等函式1捲上μ函式得到單位函式ϵ,於是有:

S(n)=1i=2nS(ni)
只需要計算形如nx的數的S值。對小於等於n23的數使用線性篩預處理,對於其它的數使用分塊來計算。
積分算得時間複雜度:
On13x=1nxdx=O(n23)
更多積性函式字首和相關請參考2016年任之洲的國家集訓隊論文《積性函式求和的幾種方法》以及唐老師的部落格

程式碼實現

第一道杜教篩,寫得及其醜。
關於那個S(nx

)怎麼存,對於nx開一個表直接存,對於nx>n另外開一個表存在x這個位置。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long LL;

const int L=4641600;
const int N=100050;
const int M=N<<1;

int mu[L],sum[L],pri[L],f[L];
bool
mark[L]; LL S[2][N]; LL num[M]; int l,s,cnt; LL a,b; int id(LL x,LL n){return x>s?n/x:x;} void pre(LL n) { memset(mark,0,sizeof mark),memset(pri,0,sizeof pri); l=pow(n,2.0/3.0),s=sqrt(n); mark[1]=1,f[1]=1,mu[1]=1; for (int i=2;i<=l;++i) { if (!mark[i]) mark[i]=1,f[pri[++pri[0
]]=i]=i,mu[i]=-1; for (int j=1,k;j<=pri[0];++j) { if (1ll*pri[j]*i>l) break; mark[k=pri[j]*i]=1,f[k]=pri[j]; mu[k]=f[k]==f[i]?0:-mu[i]; if (!(i%pri[j])) break; } } for (int i=1;i<=l;++i) sum[i]=sum[i-1]+mu[i]; cnt=0; for (LL st=1,en,x;st<=n;st=en) num[++cnt]=x=n/st,en=n/x+1; } LL sieve(LL n) { pre(n); for (int sign,idn,i=cnt;i>=1;--i) { idn=id(num[i],n),sign=idn==num[i]; if (num[i]<=l) { S[sign][idn]=sum[num[i]]; continue; } LL res=1; for (LL st=2,en,x;st<=num[i];st=en) { x=num[i]/st,en=num[i]/x+1; int idn_=id(x,n),sign_=idn_==x; res-=(en-st)*S[sign_][idn_]; } S[sign][idn]=res; } return S[0][1]; } int main() { freopen("mobius.in","r",stdin),freopen("mobius.out","w",stdout); scanf("%lld%lld",&a,&b); printf("%lld\n",sieve(b)-sieve(a-1)); fclose(stdin),fclose(stdout); return 0; }