1. 程式人生 > >【51Nod1244】莫比烏斯函式之和

【51Nod1244】莫比烏斯函式之和

Description

莫比烏斯函式,由德國數學家和天文學家莫比烏斯提出。梅滕斯(Mertens)首先使用μ(n)(miu(n))作為莫比烏斯函式的記號。具體定義如下:
如果一個數包含平方因子,那麼miu(n) = 0。例如:miu(4), miu(12), miu(18) = 0。
如果一個數不包含平方因子,並且有k個不同的質因子,那麼miu(n) = (-1)^k。例如:miu(2), miu(3), miu(30) = -1,miu(1), miu(6), miu(10) = 1。

給出一個區間[a,b],S(a,b) = miu(a) + miu(a + 1) + …… miu(b)。
例如:S(3, 10) = miu(3) + miu(4) + miu(5) + miu(6) + miu(7) + miu(8) + miu(9) + miu(10)
= -1 + 0 + -1 + 1 + -1 + 0 + 0 + 1 = -1。

Solution

先寫出表示式

f(n)=ni=1μ(i)
所以ans=f(b)f(a1)
一開始不知道怎麼辦。。。但是突然想到了一個東西。
d|nμ(d)=0但是如果n=1,這個東西就等於1。
發現了求和性質和1的這個特殊性。
ni=1d|imu(d)=1
熟練掌握內層外移的話,這種東西只可以轉化的。
這個式子=nd=1ndi=1mu(i)
再帶入f陣列轉化一下。
這個式子=nd=1f(nd)
因為nd=2f(nd)+f(n)=1
所以f(n)=1nd=2f(nd)
然後再分塊一下。

會超時!

打一個記憶化搜尋。像模擬連結串列一樣。

注意!

空間不要都要long long,會爆啊!

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=5000007
,mo=857777; int i,j,k,l,t,n,m,miu[maxn],p[maxn],sum[maxn]; ll a,b,ans,chang[mo]; int next[mo],last[mo],first[mo],num; bool bz[maxn]; void add(int x,int y,ll z){ last[++num]=y;next[num]=first[x];first[x]=num;chang[num]=z; } int suan(ll x){ int i,j=x%mo,k=1;ll l=2,r; if(x<=maxn)return sum[x]; rep(i,j)if(last[i]==x)return chang[i]; while(l<=x){ r=x/(x/l); k-=(r-l+1)*suan(x/l); l=r+1; } add(j,x,k); return k; } int main(){ scanf("%lld%lld",&a,&b); miu[1]=1; fo(i,2,maxn){ if(!bz[i]){ miu[i]=-1; p[++p[0]]=i; } fo(j,1,p[0]){ t=i*p[j];if(t>maxn)break;bz[t]=1; if(!(i%p[j])){break;} miu[t]=-miu[i]; } } fo(i,1,maxn)sum[i]=sum[i-1]+miu[i]; printf("%d",suan(b)-suan(a-1)); }