线性求 ii 的做法

线性求 ii 的做法

方便起见,我们记 fi=iii 的最小质因子为 p=minp(i),第 i 个质数为 pri

对于质数 p 用快速幂计算,这里复杂度 O(nlnnlogn)

对于合数 i=pqfi=(pq)pq=fpqfqp,由于 pn,因此我们可以 BSGS 预处理 fp1B 以及 (fpB)1B

考虑如何快速计算后半部分。回顾线性筛的流程,i 是在外层枚举到 q,内层枚举到 p 时计算,因此对于 q 而言,它计算的东西依次为 fqpr1,pr2,,指数增量是 prime gap,即 O(lnn),因此可以预处理出 fq1,2,,lnnq,这里复杂度 O(n/qln(n/q)+lnnq),累加起来 O(n)(实测中,由于枚举到 p=minp(q) 就会 break,所以常数极小)。

上面这个 1,2,,lnnq 也可以用 BSGS 优化到 lnnq,不过没啥影响,说不定还跑不过直接暴力。​

时间复杂度 O(n)


下面是一些实验性代码:

O(n) 的实现

const int N = 100000005;
const int SN = ((int)sqrt(N) + 5);
const int mod = 998244353;

int qpow(int a, int b) {
    int res = 1;
    while (b > 0) {
        if (b & 1) res = 1ull * res * a % mod;
        a = 1ull * a * a % mod, b >>= 1;
    }
    return res;
}

int bsgs1[SN][SN], bsgs2[SN][SN];
bool vis[N];
int f[N], pr[N / 10], len;
int powers[250], S;

void sieve(int n) {
    f[1] = 1;
    const int B = sqrt(n);
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            pr[++len] = i;
            f[i] = qpow(i, i);
            if (i <= B) {
                bsgs1[i][0] = 1;
                for (int j = 1; j <= B; j++)
                    bsgs1[i][j] = 1ull * bsgs1[i][j - 1] * f[i] % mod;
                bsgs2[i][0] = 1;
                for (int j = 1; j <= B; j++)
                    bsgs2[i][j] = 1ull * bsgs2[i][j - 1] * bsgs1[i][B] % mod;
            }
        }
        powers[0] = 1;
        int cur = 1, gap = 0;
        for (int j = 1; j <= len && i * pr[j] <= n; j++) {
            vis[pr[j] * i] = 1;
            int num = i * pr[j], now = pr[j] - pr[j - 1];
            if (now > gap) {
                S++;
                for (int ex = gap + 1; ex <= now; ex++)
                    powers[ex] = 1ull * powers[ex - 1] * f[i] % mod;
                gap = now;
            }
            cur = 1ull * cur * powers[now] % mod;
            f[num] = 1ull * bsgs1[pr[j]][i % B] * bsgs2[pr[j]][i / B] % mod * cur % mod;
            if (i % pr[j] == 0) break;
        }
    }
    fprintf(stderr, "S = %d\n", S);
    fprintf(stderr, "time used = %.10f\n", (clock()) / 1. / CLOCKS_PER_SEC);
}

实验数据:

  • n=1070.188s
  • n=1081.816s

O(nloglnn) 的实现(即 prime gap 每次暴力快速幂计算):

const int N = 100000005;
const int SN = ((int)sqrt(N) + 5);
const int mod = 998244353;

int qpow(int a, int b) {
    int res = 1;
    while (b > 0) {
        if (b & 1) res = 1ull * res * a % mod;
        a = 1ull * a * a % mod, b >>= 1;
    }
    return res;
}

int bsgs1[SN][SN], bsgs2[SN][SN];
bool vis[N];
int f[N], pr[N / 10], len;

void sieve(int n) {
    f[1] = 1;
    const int B = sqrt(n);
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) {
            pr[++len] = i;
            f[i] = qpow(i, i);
            if (i <= B) {
                bsgs1[i][0] = 1;
                for (int j = 1; j <= B; j++)
                    bsgs1[i][j] = 1ull * bsgs1[i][j - 1] * f[i] % mod;
                bsgs2[i][0] = 1;
                for (int j = 1; j <= B; j++)
                    bsgs2[i][j] = 1ull * bsgs2[i][j - 1] * bsgs1[i][B] % mod;
            }
        }
        int cur = 1;
        for (int j = 1; j <= len && i * pr[j] <= n; j++) {
            vis[pr[j] * i] = 1;
            int num = i * pr[j];
            cur = 1ull * cur * qpow(f[i], pr[j] - pr[j - 1]) % mod;
            f[num] = 1ull * bsgs1[pr[j]][i % B] * bsgs2[pr[j]][i / B] % mod * cur % mod;
            if (i % pr[j] == 0) break;
        }
    }
    fprintf(stderr, "time used = %.10f\n", (clock()) / 1. / CLOCKS_PER_SEC);
}

实验数据:

  • n=1070.206s
  • n=1082.094s

O(nlogn) 的实现(每次暴力计算):

const int N = 100000005;
const int mod = 998244353;

int qpow(int a, int b) {
    int res = 1;
    while (b > 0) {
        if (b & 1) res = 1ull * res * a % mod;
        a = 1ull * a * a % mod, b >>= 1;
    }
    return res;
}

int f[N];

void sieve(int n) {
    f[1] = 1;
    for (int i = 2; i <= n; i++) f[i] = qpow(i, i);
    fprintf(stderr, "time used = %.10f\n", (clock()) / 1. / CLOCKS_PER_SEC);
}

实验数据:

  • n=1070.801s
  • n=1088.547s
posted @   wlzhouzhuan  阅读(1047)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示