【Bestcoder 68C】function 莫比乌斯函数

已知 n23n+2=d|nf(d)
ni=1f(i)
n <= 10^9
莫比乌斯反演得:

f(n)=d|ng(d)mu(n/d)

i=1nf(i)=i=1ng(i)j=1n/imu(j)

考虑求莫比乌斯的前缀和,设M(i)为mu的前缀和,则有:
i=1nM(n/i)=1

这样就可以先预处理100W以内的值,然后记忆化搜索来求。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <ctime>
#include <cassert>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int mod = 1000000007, M = 5000005, m2 = 1000007, inf = 1 << 30;
struct Hash {
    struct Link { int x, nex, w; } g[M];
    int pos[m2], sz;
    int Find(int x) { RepE(i, x % m2) if (g[i].x == x) return g[i].w; return inf; }
    void Add(int x, int w) { assert(sz < M); g[++ sz] = (Link) { x, pos[x % m2], w }, pos[x % m2] = sz; }
} m1;
int T, n, pri[M], u[M], tot, sum[M]; bool c[M]; LL ans;
void PreWork() {
    u[1] = sum[1] = 1;
    Rep(i, 2, M - 1) {
        if (!c[i]) pri[tot ++] = i, u[i] = -1;
        Rep(j, 0, tot - 1) {
            int k = i * pri[j];
            if (k >= M) break ;
            c[k] = 1;
            if (i % pri[j] == 0) { u[k] = 0; break ; }
            else u[k] = -u[i];
        }
        sum[i] = sum[i - 1] + u[i];
    }
}
LL S(int n) {
    LL s1 = 0;
    if (n < M) return sum[n];
    for (int i = 2, lt; i <= n; i = lt + 1) {
        lt = n / (n / i);
        if (n / i < M) s1 += (LL)sum[n / i] * (lt - i + 1);
        else {
            int k = m1.Find(n / i);
            if (k == inf) {
                m1.Add(n / i, k = S(n / i));
            }
            s1 += (LL)k * (lt - i + 1);
        }
    }
    return 1 - s1;
}
LL G(LL n) {
    if ((2 * n + 1) % 3 == 0) return ((n * (n + 1) / 2) % mod) * ((2 * n + 1) / 3) % mod;
    return ((n * (n + 1) / 6) % mod) * (2 * n + 1) % mod;
}
int main()
{
    scanf ("%d", &T);
    PreWork();
    while (T --) {
        scanf ("%d", &n);
        ans = 0;
        LL s0 = 0;
        for (int i = 1, lt; i <= n; i = lt + 1) {
            lt = n / (n / i);
            LL k = S(n / i), z = (G(lt) - 3LL * (lt + 1) * lt / 2 + 2LL * lt) % mod;
            (ans += (z - s0 + 3LL * mod) * k) %= mod;
            s0 = z;
        }
        printf("%I64d\n", (ans + mod) % mod);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值