[51nod1244]莫比烏斯函數之和

題意:求區間[a,b]的莫比烏斯函數µ之和。  a,b<=$10^{11}$php

題解:很容易把區間求和改成求前綴和並求差,即要求$M(x)=\sum_{1}^{n}\mu(x)$考慮化簡ios

莫比烏斯函數存在一個性質,也就是$\sum_{d|n}^{ } \mu(d)= [n=1]$,那麼$\sum_{i=1}^{n}\sum_{d|i}^{ } \mu(d)= 1$ 函數

這個式子比較複雜,咱們轉而考慮對於每個d,它被計算了多少次,也就是$\sum_{i=1}^{n}\sum_{d=1}^{\lfloor n/i \rfloor} \mu(d)$ ,這個式子=$\sum_{i=1}^{n}M(\lfloor n/i \rfloor)$=1  因此說,$M(n)=1-\sum_{i=2}^{n}M(\lfloor n/i \rfloor)$網站

$\lfloor n/i \rfloor$/i只有$\sqrt(n)$種,複雜度在預處理出前$k=n^{\frac{2}{3}}$的M值時最小,而後記憶化搜索能夠在$O(n^{\frac{2}{3}})$內解決。url

咱們發如今計算中$\lfloor n/i \rfloor$有不少會被重複計算,因此能夠手寫一個map來極大地提高效率。spa

第一次用latex寫公式,還好有個很牛逼的網站.net

估計是模數選的好,隨便寫寫RANK1啦。code

#include<iostream> #include<cstdio> #include<cmath>
#define MAXN 5000000
#define mod 2333333
#define ll long long
using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();} return x*f; } struct my_map{ ll x;int ans,next; }e[MAXN+5]; int f[MAXN+5],ans=0,num=0,s[MAXN],head[mod+5]; bool b[MAXN+5]; void ins(ll x,int sum) { int j=x%mod; e[++num]=(my_map){x,sum,head[j]}; head[j]=num; } int calc(ll n) { if(n<=MAXN) return f[n]; for(int i=head[n%mod];i;i=e[i].next) if(e[i].x==n)return e[i].ans; int sum=1,q=sqrt(n); for(int i=2;i<=q;i++) sum-=calc(n/i); q=n/(q+1); for(int i=1;i<=q;i++) sum-=(n/i-(n/(i+1)))*calc(i); ins(n,sum); return sum; } int main() { f[1]=1;b[1]=1; for(int i=2;i<=MAXN;i++) { if(!b[i]) s[++num]=i,f[i]=-1; for(int j=1;j<=num&&s[j]*i<=MAXN;j++) { int t=s[j]*i; b[t]=1; if(i%s[j]==0){f[t]=0;break;} f[t]=-f[i]; } } for(int i=2;i<=MAXN;i++) f[i]+=f[i-1]; num=0; ll x=read();ans-=calc(x-1); x=read();ans+=calc(x); cout<<ans; return 0; }
相關文章
相關標籤/搜索