loj6053 简单的函数(min25筛)

本文详细解析了一种求解特定条件下质数和合数和的算法,通过预处理质数及其前缀和,结合数论分块与递归策略,实现了高效求解。适用于数学竞赛和算法挑战。

题目

对于100%的数据,n<=1e10

思路来源

https://www.cnblogs.com/cjyyb/p/9185110.html

题解

首先,考虑质数,即c=1的情形,f(2)=3,其余f(p)=p-1,

这里就先统一f(p)=p-1处理,然后对第一个质数特判加2


g(n,j)=\sum_{i=1}^nF(i)[i\in P\ or \ min(p)>P_j,p|i,p\in P\ ]

g(n,j)表示1n中,满足i是质数,或最小素因子大于第j个质数的,F(i)的和

由于f(p)=p-1,所以这里拆成两部分,

g[]用来算前面一项,质数p的前缀和,h[]用来算后面一项,质数的个数即1的前缀和

两个数组求法是一样的,然后第二维可以根据增序迭代滚动掉

 

那么g(n,0)=\sum_{i=2}^{n}f(i)

即把所有数都先当质数来算,求得一个假的值,后续再不断的迭代,把那些合数的减掉

初始时,g(i)=\frac{i*(i-1)}{2}h(i)=i-1

g(n,j)=\left\{\begin{matrix} g(n,j-1)&P_j^2> n\\ g(n,j-1)-F(P_{j})[g(\frac{n}{P_j},j-1)-g(P_j-1,j-1)]&P_j^2\le n \end{matrix}\right.

实际上,本题只用到第二行的式子,且根据g的定义,

g(P_{j}-1,j-1)里只有那些质数的前缀和,所以有

g(n,j)=g(n,j-1)-F(P_{j})[g(\frac{n}{P_j},j-1)-\sum_{i=1}^{j-1}F(P_{j})]

根据这个固定转移式,发现需要n/Pj的值,那么套一下数论分块,

先把第一维的值弄出来,个数约为2\sqrt n个,

则用id1和id2记录一下这些下标,相当于存到两个大小为\sqrt n的桶里,来进行离散化

    for (ll l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        w[++c] = n / l;
        h[c] = (w[c] - 1) % mod;
        g[c] = (w[c] % mod) * ((w[c] + 1) % mod) % mod * inv2 % mod - 1;
        if (w[c] <= Sqr)
            id1[w[c]] = c;
        else
            id2[r] = c;
    }

然后,正序处理g,迭代|P|次,即tot次,

最后的g[c]为g(n,|P|),即为所求,

    for (int j = 1; j <= tot; ++j) {
        for (int i = 1; i <= c && pri[j] * pri[j] <= w[i]; ++i) {
            int k = (w[i] / pri[j] <= Sqr) ? id1[w[i] / pri[j]] : id2[n / (w[i] / pri[j])];
            g[i] =(g[i]- 1ll * pri[j] * (g[k] - sp[j - 1]) % mod) % mod;
            h[i] =(h[i]-(h[k] - j + 1))% mod;
        }
    }

然后,考虑求S,这里采用递归,并且不需要记忆化

先考虑一下S(n,j)的初值,也就是所有满足条件的质数的答案

前期做题还不熟练,把概念再粘过来

S(n,j)=\sum_{i=1}^nF(i)[min(p)\ge P_j,p\in P,p|i\ ]

i满足最小质因数大于等于P_{j}F(i)之和

这个答案是g(n,|P|)-h(n,|P|)-\sum_{i=1}^{j-1}{P_{i}-1}

然后根据S(n,j)=g(n,|P|)-\sum_{i=1}^{j-1}f(P_i)+\sum_{k\ge j}\sum_{e}(F(P_k^e)S(\frac{n}{P_k^e},k+1)+F(P_k^{e+1}))

需要枚举后面两层sum,即一层最小质因子Pk,另一层是其幂次e,递归求答案

然后j=1时,需要把质数为2的特殊考虑,把和式的值+2

代码

loj神奇的自动缩进

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10, mod = 1e9 + 7, inv2 = (mod + 1) / 2;
ll n, Sqr, w[N];
ll pri[N], id1[N], id2[N], h[N], g[N], c;
bool zs[N];
int tot, sp[N];
void pre(int n) {
    zs[1] = true;
    for (int i = 2; i <= n; ++i) {
        if (!zs[i]) {
            pri[++tot] = i;
            sp[tot] = (sp[tot - 1] + i) % mod;
        }
        for (int j = 1; j <= tot && i * pri[j] <= n; ++j) {
            zs[i * pri[j]] = true;
            if (i % pri[j] == 0)
                break;
        }
    }
}
int S(ll x, int y) {
    if (x <= 1 || pri[y] > x)
        return 0;
    int k = (x <= Sqr) ? id1[x] : id2[n / x], ret = (g[k] - sp[y - 1] - h[k] + y - 1) % mod;
    if (y == 1)
        ret += 2;
    for (int i = y; i <= tot && 1ll * pri[i] * pri[i] <= x; ++i) {
        ll t1 = pri[i], t2 = 1ll * pri[i] * pri[i];
        for (int e = 1; t2 <= x; ++e, t1 = t2, t2 *= pri[i]) {
            ret = (ret + ((1ll * S(x / t1, i + 1) * (pri[i] ^ e) % mod + (pri[i] ^ (e + 1)) % mod))) % mod;
        }
    }
    return ret;
}
int main() {
    scanf("%lld", &n);
    Sqr = sqrt(n);
    pre(Sqr);
    for (ll l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        w[++c] = n / l;
        h[c] = (w[c] - 1) % mod;
        g[c] = (w[c] % mod) * ((w[c] + 1) % mod) % mod * inv2 % mod - 1;
        if (w[c] <= Sqr)
            id1[w[c]] = c;
        else
            id2[r] = c;
    }
    for (int j = 1; j <= tot; ++j) {
        for (int i = 1; i <= c && pri[j] * pri[j] <= w[i]; ++i) {
            int k = (w[i] / pri[j] <= Sqr) ? id1[w[i] / pri[j]] : id2[n / (w[i] / pri[j])];
            g[i] = (g[i] - 1ll * pri[j] * (g[k] - sp[j - 1]) % mod) % mod;
            h[i] = (h[i] - (h[k] - j + 1)) % mod;
        }
    }
    int ans = S(n, 1) + 1;
    printf("%d\n", (ans + mod) % mod);
    return 0;
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值