转载请注明出处,谢谢http://blog.csdn.net/bigtiao097?viewmode=contents
题意:
s(n)=∑i=1nμ(i)
μ(n) 是莫比乌斯函数
给定a,b,求 s(b)−s(a) (2≤a<b≤1010)
思路:
这应该就是之前听说过的杜教筛,第一次做这种题
给大家推荐唐老师的一篇文章,个人感觉讲的非常好,我就是在这里学的
莫比乌斯函数的一个性质是这样的
∑d|nμ(d)={10 n=1 n>1
所以就有
1=∑i=1n[i=1]=∑i=1n∑d|iμ(d)=∑i=1n∑d=1⌊ni⌋μ(d)=∑i=1ns(⌊ni⌋)(1)(2)
这样就得到了
s(n)=1−∑i=2ns(⌊ni⌋)
感觉上面式子中最难理解的地方就是
(1)→(2)
的过程了,这里稍微解释一下:
可以这样理解
∑i=1n∑d|iμ(d)=∑i=1n∑d|iμ(id)=∑i=1n∑j=1⌊ni⌋μ(j)
第二个式子中的 i 理解为枚举每一个数,最后一个式子中的 i 理解为枚举每一个因子
对于
s(n)=1−∑i=2ns(⌊ni⌋)
然后就可以递归求解了,顺便加记忆化,顺便加上分段优化来求了
关于复杂度的问题,具体可以看上面推荐的文章,这里稍微说一下
- 如果我们先提前筛选出了 n23 的 μ(n) 的前缀和,总体复杂度大概是 O(n23)
- 如果没有提前处理一下,直接递归求解的话, 总体复杂度大概是 O(n34)
然后再说一下递归过程中记忆化的时候用的什么数据结构
- 手写hash表
- map,这个理论上的复杂度比较高,因为map中时排序的,而我们根本不需要排序来保存,用hash表更好,但是有时候就很玄学,用map过的了,手写hash表过不了(大概这种情况不多吧),这个题就是手写hash表可以过(1687ms),但是用map的话直接TLE(4140ms)
- unordered_map(c++11)新特性,交题的时候记得选c++11,否则可能会CE
这个其实就是一个hash表,能取代手写的hash表,减少代码量,有时候比手写的hash表跑的还快,这个题就是 unordered_map更快(1375 ms)
以上三个到底用哪个不好说,不过个人推荐优先级unordered_map>手写hash表>>map
下面提供unordered_map和手写hash表的代码,仅供参考
具体代码如下:
Result:Accepted
Memory:29952 KB
Time :1687 ms
#include<bits/stdc++.h>
const int maxn = 4641590;//maxn = n^(2/3)
using namespace std;
typedef long long ll;
const int HASH_MOD=987654;
int mu[maxn];
bool vis[maxn];
int p[maxn];
ll l,r;
ll key[HASH_MOD], val[HASH_MOD];
int head[HASH_MOD], nxt[HASH_MOD];
struct Hash{
int tot;
void init(){
memset(head, -1, sizeof(head));
tot = 0;
}
ll inst(ll x, ll y){
int k = x % HASH_MOD;
key[tot] = x;
val[tot] = y;
nxt[tot] = head[k];
head[k] = tot++;
}
ll finda(ll x){
int k = x % HASH_MOD;
for(int i = head[k]; i != -1; i = nxt[i])
if(key[i] == x)
return val[i];
return -1;
}
}hs;
int calc(ll x)
{
if (x<maxn) return mu[x];
if(hs.finda(x)!= -1)
return hs.finda(x);
int ans = 1;
for(ll l=2,r; l<=x; l=r+1)
{
r=x/(x/l);
ans-=calc(x/l)*(r-l+1);
}
hs.inst(x,ans);
return ans;
}
void mobius()
{
mu[1]=1;
for(int i=2;i<maxn;i++)
{
if (!vis[i])
{
p[++p[0]]=i;
mu[i]=-1;
}
for(int j=1;j<=p[0];j++)
{
int k=i*p[j];
if (k>=maxn) break;
vis[k]=1;
if (!(i%p[j])) break;
mu[k]=-mu[i];
}
}
for(int i=1;i<maxn;i++)
mu[i]+=mu[i-1];
}
int main()
{
mobius();
hs.init();
scanf("%lld%lld",&l,&r);
printf("%d",calc(r)-calc(l-1));
}
Result:Accepted Memory:26224 KB Time :1375 ms
#include<bits/stdc++.h>
const int maxn = 4641590;//maxn = n^(2/3)
using namespace std;
typedef long long ll;
int mu[maxn];
bool vis[maxn];
int p[maxn];
ll l,r;
unordered_map<ll,ll> mp;
int calc(ll x)
{
unordered_map<ll,ll>::iterator it;
if (x<maxn) return mu[x];
if((it=mp.find(x))!= mp.end())
return it->second;
int ans = 1;
for(ll l=2,r; l<=x; l=r+1)
{
r=x/(x/l);
ans-=calc(x/l)*(r-l+1);
}
return mp[x]=ans;
}
void mobius()
{
mu[1]=1;
for(int i=2;i<maxn;i++)
{
if (!vis[i])
{
p[++p[0]]=i;
mu[i]=-1;
}
for(int j=1;j<=p[0];j++)
{
int k=i*p[j];
if (k>=maxn) break;
vis[k]=1;
if (!(i%p[j])) break;
mu[k]=-mu[i];
}
}
for(int i=1;i<maxn;i++)
mu[i]+=mu[i-1];
}
int main()
{
mobius();
scanf("%lld%lld",&l,&r);
printf("%d",calc(r)-calc(l-1));
}