题意:求区间[a,b]的莫比乌斯函数µ之和。 a,b<=$10^{11}$
题解:很容易把区间求和改为求前缀和并求差,即要求$M(x)=\sum_{1}^{n}\mu(x)$考虑化简
莫比乌斯函数存在一个性质,也就是$\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}})$内解决。
我们发现在计算中$\lfloor n/i \rfloor$有很多会被重复计算,所以可以手写一个map来极大地提升效率。
第一次用latex写公式,还好有个很牛逼的网站
估计是模数选的好,随便写写RANK1啦。
#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;
}
来源:oschina
链接:https://my.oschina.net/u/4392886/blog/4255288