Live2D

Note -「Min_25 筛」“你就说这素因子你要不要吧?你要不要?”

  赛上想写,Track Lost 了属于是。

Intro

  Min_25 筛是用于求积性函数前缀和,同时顺带求出一些“有意思”的信息的筛法。

  一些记号约定

  • P 为素数集,对于以 p 为记号的数,有 pP
  • pi 表示第 i 小的素数。特别地,p0=1
  • lpf(n) 表示 n 的最小素因子。
  • a/b=ab.

Algorithm

  明确我们的目标:对于积性函数 f(n),求出

i=1nf(i).

此后,我们用 m 来表示一般的求和上标,而 n 恒为问题中待求的前缀值。


  Min_25 筛分两步走。第一步:对于所有 xD={n/ii[1,n]},求出 pxf(p)

  这里需要运用 Min_25 筛的核心思想:首先承认所有合数为素数,然后逐步筛掉它们,修正得到正确答案。形式地,令 fP(x) 表示 f(p) 处关于 p 的多项式,当然我们需要把 fP(x) 的定义域从 P 强行钦定为 N;最后,定义 G(m,i)

G(m,i)=j=2m[jPlpf(j)>pi]fP(j).

它表示对于 2m,只用前 i 个素数做埃筛后,承认剩下的数都是素数,得到的 f 之和。可见我们的目标就是求出所有 G(x,π(n))。尝试写出 G(m,i) 的递推式:

G(m,i)=G(m,i1)j=2m[jPlpf(j)=pi]fP(j).

如何优化掉和式?为了将和式转化成 G 的形式,自然的想法是令 jj/pi,那么此时就必须追加一个条件:fP(x) 为完全积性函数。借此进一步转化:

G(m,i)=G(m,i1)fP(pi)[G(m/pi,j1)j<ifP(pj)].

注意 j<iπ(n),所以 P(m)=i=1mfP(pi) 可以直接预处理出来。复杂度待会儿说√

  可见,就算仅使用 Min_25 筛的第一步,也能解决一些问题。例如令 f(n)=1,就能计算 π(n)


  第二步:对于所有 xD,求出 i=2xf(i)

  还是利用同样的思想,定义 F(m,i)

F(m,i)=j=2m[jPlpf(j)>pi]f(j).

它表示素数以及所有被 pi+1..π(n) 筛掉的合数的 f 之和,m 的最终答案即为 F(m,0)。类似与 G 的转移,不过由于 f 不一定完全积性,我们不得不枚举 pi 的指数,有

F(m,i)=F(m,i+1)+j1,pij+1mf(pij)[F(m/pj,i+1)jifP(pj)]+f(pij+1).

注意 f(pi) 早已计算过,所以和式的第二项从 f(pi2) 开始累加。

  这里还有一种两层和式的递推方式,据说会引出 O(n1ϵ) 的递归求解算法。我不会证,而且我一写出来就是这种一层和式,你让我怎么硬生生再套一层,所以略过,抱歉 qwq。


  对于 F,G 的状态复杂度,有

T(n)=i=1nπ(n/i)+x=1nπ(i)=i=1nO(n/ilnn/i)+O(ilni)=O(0n(n/x)1212ln(n/x)dx)=O(0n(n/x)1214lnndx)=O(n34lnn).

为了方便理解,log 都确切地写作 ln 啦。

  这里有个疑点,为什么 F 的递推形式转移不影响复杂度。没找到资料 qwq。

  UPD: 悟了,前常数个 j 直接放成 O(n34lnn),直到后面的 j 全部放成 ln 倍的某一复杂度都比不过,就能证了。

  对于空间复杂度,滚动 iF(m,i)G(m,i) 都只需要记录 O(n)n/i 的点值。

  注意:为保证复杂度,不能访问任何非法状态,否则退化成 O(n1ϵ)


  UPD: 放个实例代码吧方便复习。Problem link.

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef long long LL;

const int MAXSN = 316228;
int pn, pr[MAXSN + 5];
bool npr[MAXSN + 5];

inline void init() {
    rep (i, 2, MAXSN) {
        if (!npr[i]) pr[++pn] = i;
        for (int j = 1, t; j <= pn && (t = i * pr[j]) <= MAXSN; ++j) {
            npr[t] = true;
            if (!(i % pr[j])) break;
        }
    }
}

LL n, sn, idx[MAXSN * 2 + 5];
LL f[MAXSN * 2 + 5], g[MAXSN * 2 + 5];

// #define getF(x) f[(x) <= sn ? (x) : sn + n / (x)]
inline LL& getF(const LL x) { return f[x <= sn ? x : sn + n / x]; }
// #define getG(x) g[(x) <= sn ? (x) : sn + n / (x)]
inline LL& getG(const LL x) { return g[x <= sn ? x : sn + n / x]; }

inline LL calc(const LL tn) {
    if (tn <= 1) return 0;
    ::n = tn, ::sn = sqrt(tn), idx[0] = 0;
    for (LL l = 1; l <= n; l = n / (n / l) + 1) idx[++idx[0]] = n / l;
    std::sort(idx + 1, idx + idx[0] + 1);

    rep (i, 1, idx[0]) getF(idx[i]) = idx[i] - 1;
    rep (i, 1, pn) {
        if (1ll * pr[i] * pr[i] > n) break;
        per (j, idx[0], 1) {
            if (1ll * pr[i] * pr[i] > idx[j]) break;
            getF(idx[j]) -= getF(idx[j] / pr[i]) - (i - 1);
        }
    }
    rep (i, 1, idx[0]) g[i] = 0;

    per (i, pn, 1) {
        per (j, idx[0], 1) {
            if (1ll * pr[i] * pr[i] > idx[j]) break;
            LL pw = pr[i], &cur = getG(idx[j]);
            for (int k = 1; pw * pr[i] <= idx[j]; ++k, pw *= pr[i]) {
                cur += getG(idx[j] / pw) + pr[i] * (getF(idx[j] / pw) - i + 1);
            }
        }
    }
    return getG(n);
}

int main() {
    init();
    LL l, r;
    scanf("%lld %lld", &l, &r);
    printf("%lld\n", calc(r) - calc(l - 1));
    return 0;
}

posted @   Rainybunny  阅读(102)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示