51Nod 1244 莫比乌斯函数之和

转载请注明出处,谢谢http://blog.csdn.net/bigtiao097?viewmode=contents

题意

s(n)=i=1nμ(i)

μ(n) 是莫比乌斯函数
给定a,b,求 s(b)s(a)  (2a<b1010)

思路

这应该就是之前听说过的杜教筛,第一次做这种题
给大家推荐唐老师的一篇文章,个人感觉讲的非常好,我就是在这里学的
莫比乌斯函数的一个性质是这样的

d|nμ(d)={10 n=1 n>1

所以就有
1=i=1n[i=1]=i=1nd|iμ(d)=i=1nd=1niμ(d)=i=1ns(ni)(1)(2)

这样就得到了
s(n)=1i=2ns(ni)

感觉上面式子中最难理解的地方就是 (1)(2) 的过程了,这里稍微解释一下:
可以这样理解

i=1nd|iμ(d)=i=1nd|iμ(id)=i=1nj=1niμ(j)

第二个式子中的 i  理解为枚举每一个数,最后一个式子中的 i  理解为枚举每一个因子

对于

s(n)=1i=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));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值