hdu6134-莫比乌斯反演+思维



题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6134


题意:

让你求


直接枚举求前缀和是o(n^2)的复杂度,肯定会超时,应该想办法优化的复杂度,

然后求前缀和,就可以得到要求的f(n)


,则


根据莫比乌斯反演,求得



计算时,直接枚举复杂度太高,可以枚举j和t,,这样时间复杂度为

n/1 + n/2 + n/3 + ... + n/n = n * (1 + 1/2 + 1/2 + ... + 1/n) ≈ n * log(n)


可以知道,每个t对区间有贡献,所以让g[(t - 1) * j + 1] += t, g[t * j] -= t, 

然后g求前缀和即得到所求的g。再处理出莫比乌斯函数就能够求f了。


时直接枚举i,d复杂度为o(n * sqrt(n)),会超时,

应该枚举d,然后枚举i = k * d,这样复杂度为n/1 + n/2 + n/3 + ... n/n = n * (1 + 1/2 + 1/3 + ... + 1/n) ≈ o(n * log(n)),

总的时间复杂度为o(n * log(n))



代码:

# include <iostream>
# include <algorithm>
# include <cstdio>
# include <cstring>
# include <cmath>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 1e6 + 5;
int vis[maxn];
int pri[maxn];
int mu[maxn];
int g[maxn];
int f[maxn];
int n;
int len;

void mobius() {
    memset(vis, 0, sizeof vis);
    mu[1] = 1; len = 0;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) {
            pri[len++] = i;
            mu[i] = -1;
        }
        for (int j = 0; j < len && i * pri[j] < maxn; ++j) {
            vis[i * pri[j]] = 1;
            if (i % pri[j]) mu[i * pri[j]] = -mu[i];
            else {
                mu[i * pri[j]] = 0;
                break;
            }
        }
    }
}

void init() {
    memset(g, 0, sizeof g);
    for (int j = 1; j < maxn; ++j) {
        for (int t = 1; (t - 1) * j + 1 < maxn; ++t) {
            int l = max((t - 1) * j + 1, j), r = min(t * j + 1, maxn - 1);
            g[l] = ((ll)g[l] + t) % mod;
            g[r] = (((ll)g[r] - t) % mod + mod) % mod;
        }
    }

    for (int i = 1; i < maxn; ++i) {
        g[i] = ((ll)g[i] + g[i - 1]) % mod;
    }
    memset(f, 0, sizeof f);
    for (int d = 1; d < maxn; ++d) {
        for (int i = d; i < maxn; i += d) {
            f[i] = ((ll)f[i] + (ll)mu[d] * g[i / d] % mod) % mod;
        }
    }
    for (int i = 1; i < maxn; ++i) {
        f[i] = ((ll)f[i] + f[i - 1]) % mod;
    }
}

int main(void)
{
    mobius(); init();
    while (~scanf("%d", &n))
        printf("%d\n", f[n]);

    return 0;
}

/*

100
1000
500
666
30
10000
100000
500000

100
17744
1000
2473330
500
566051
666
1041802
30
1275
10000
317187476
100000
716969323
500000
224647420
123456
986099818
871496
888437745
800000
408863358
600000
873233747
->872733683
850000
177648068
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值