min25筛学习笔记

min25筛

min25筛用于求一类数论函数的前缀和,适用于函数在素数处的取值可以用一个关于此素数的多项式来表示的数论函数。

处理质数部分

这部分我们需要解决pprimef(p),这里简单起见,假设f(p)=pt

si表示前i个质数之和,用LPFi表示i的最小质因子,pi表示第i个质数,设g(n,i)=k=1n[LPFk>pikprime]kt

因此有:$$g(n,0)=\sum\limits_{k=1}nkt$$

假设n内有cnt个质数,那么

g(n,cnt)=k=1n[kprime]kt

因此我们的目标就是求解g(n,cnt),接下来考虑转移,即g(n,i)g(n,i1)的关系。注意到:

g(n,i1)=k=1n[LPFk>pikprime]kt+k=1n[LPFk=pikprime]kt

因此有转移:

  • pipi>n

    g(n,i)=g(n,i1)

  • pipin

g(n,i)=g(n,i1)k=1n[LPFk=pikprime]kt=g(n,i1)pitk=1npi[LPFkpi]kt=g(n,i1)pit[g(npi,i1)si1]

处理完整问题

S(n,i)=k=1n[LPFk>pi]f(k),则我们的目标是求解S(n,0)

考虑如何转移:

  • pipi>n

S(n,i1)=g(n,i1)si1

  • pipin

S(n,i1)=pjenjif(pje)([e>1]+k=1npje[LPFk>pj]f(k))+g(n,cnt)si1=g(n,cnt)si1+pjenjif(pje)(S(npje,j)+[e>1])(1)=S(n,i)+pienf(pie)(S(npie,i)+1)(2)

可以直接按照(1)式暴力求解,也可以按照(2)式仿照质数部分DP递推求解。

时间复杂度分析

对于质数部分的求解,我们只关注t{nk|kN}g(t,i)的取值,而只有当pipit时才会产生转移。因此复杂度可以估计为:

i=1nilni+i=1nnilnni=O(1nnxlog2nxdx)=O(n34log2n)

对于第二部分的复杂度分析类似,值得一提的是,按照(1)式暴力求解的时间复杂度也是对的,而且这种写法更加简洁。

一些例子

  • i=1ni=1nfk(gcd(i,j))其中f(x)表示x的次大质因子,重复的质因子计算多次,例如f(4)=f(6)=2规定f(1)=0,f(p)=1其中p为质数。n,k2×109

i=1ni=1nfk(gcd(i,j))=d=1ni=1ndj=1nd[gcd(i,j)=1]fk(d)=d=1nfk(d)i=1ndμ(i)ndi2g(n)=i=1nμ(i)ni2=d=1nfk(d)g(nd)gfkg+fS(n,i)=j=1n[LPFj>pijprime]fk(j)pipi>n,S(n,i1)=j=1n[jprime]pipin,S(n,i1)=S(n,i)+S(npi,i1)j=1npi[jprime]+pi((j=1npi[jprime])i+1)nmin25

code:

#include <cstdio>
#include <iostream>
#define int unsigned int

using namespace std;

const int M = 3e5 + 5;

int prime[M], cnt = 0, a[M], g[M << 1], f[M << 1], primeMi[M];
int n, k, v[M << 1], ss = 0, mu[M], mus[M];

inline int getIdx(int o) { return (o > M) ? (M + n / o) : o; }

int mi(int o, int p) {
    int yu = 1;
    while (p) {
        if (p & 1) yu *= o;
        o *= o;
        p >>= 1;
    }
    return yu;
}

int cal(int o) {
    if (o < M) return mu[o];
    if (mus[n / o]) return mus[n / o];
    int yu = 1;
    for (int l = 2, r; l <= o; l = r + 1) {
        r = o / (o / l);
        yu -= (r - l + 1) * cal(o / l);
    }
    return mus[n / o] = yu;
}

int find(int o) {
    int l = 1, r = ss, res = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (o <= v[mid] / o) {
            res = mid;
            l = mid + 1;
        } else
            r = mid - 1;
    }
    return res;
}

signed main() {
    scanf("%u%u", &n, &k);
    mu[1] = 1;
    for (int i = 2; i < M; i++) {
        if (!a[i]) {
            prime[++cnt] = i;
            primeMi[cnt] = mi(prime[cnt], k);
            mu[i] = -1;
        }
        for (int j = 1; j <= cnt && i * prime[j] < M; j++) {
            a[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                mu[i * prime[j]] = 0;
                break;
            } else {
                mu[i * prime[j]] = -mu[i];
            }
        }
    }
    mu[0] = 0;
    for (int i = 1; i < M; i++) mu[i] += mu[i - 1];
    for (int l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        v[++ss] = n / l;
        g[getIdx(n / l)] = n / l - 1;
    }
    for (int i = 1; i <= cnt; i++) {
        for (int j = 1; j <= ss; j++) {
            int t = v[j];
            if (prime[i] > t / prime[i]) break;
            g[getIdx(t)] -= g[getIdx(t / prime[i])] - i + 1;
        }
    }
    for (int j = 1; j <= ss; j++) {
        f[getIdx(v[j])] = g[getIdx(v[j])];
    }
    for (int i = cnt; i >= 1; i--) {
        int idx = find(prime[i]);
        for (int j = idx; j >= 1; j--) {
            int t = v[j];
            if (prime[i] > t / prime[i]) continue;
            int p1 = getIdx(t);
            int p2 = getIdx(t / prime[i]);
            f[p1] += f[p2] - g[p2] + primeMi[i] * (g[p2] - i + 1);
        }
    }
    int ans = 0;
    f[0] = 0;
    for (int l = 1, r; l <= n; l = r + 1) {
        int t = n / l, yu = 0;
        r = n / t;
        for (int l1 = 1, r1; l1 <= t; l1 = r1 + 1) {
            r1 = t / (t / l1);
            yu += (cal(r1) - cal(l1 - 1)) * (t / l1) * (t / l1);
        }
        ans += (f[getIdx(r)] - f[getIdx(l - 1)]) * yu;
    }
    cout << ans << endl;
    return 0;
}
  • 对于正整数n,定义如下操作每次选择pi,pj满足pi|npj|npipjn变为npipj。求有多少n以内的正整数满足可以通过若干次操作变为1n1011

1f(n,j,m,s)=i=1n[LPFi>pjmsi(m,s)]f(n,0,0,0)f(n,j,m,s)=[(m,s)]((i=1n[iprime])j)+i=j+1a=1(f(npia,i,max(m,a),s+a)+[a>1pia(m,s)])

code:

#pragma GCC optimize(2)
#include <chrono>
#include <cstdio>
#include <ctime>
#include <iostream>

using namespace std;

const int M = 317005;

int prime[M], cnt = 0, a[M], ss = 0;
long long g[M << 1], n, v[M << 1];

int getIdx(long long o) { return (o > M) ? (n / o + M) : o; }

int check(int sum, int mx) { return ((mx << 1) <= sum) && ((sum & 1) == 0); }

long long S(long long o, int p, int sum, int mx) {
    if (o < prime[p + 1]) return 0;
    long long yu = (check(sum + 1, max(mx, 1))) ? (g[getIdx(o)] - p) : 0;
    for (int i = p + 1; i <= cnt; i++) {
        if (prime[i] > o / prime[i]) break;
        int t = 1;
        for (long long j = prime[i]; j <= o; j *= prime[i], t++) {
            yu += S(o / j, i, sum + t, max(mx, t));
            yu += check(sum + t, max(mx, t)) * (t > 1);
        }
    }
    return yu;
}

signed main() {
    for (int i = 2; i < M; i++) {
        if (!a[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt && i * prime[j] < M; j++) {
            a[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    scanf("%lld", &n);
    for (long long l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        g[getIdx(n / l)] = n / l - 1;
        v[++ss] = n / l;
    }
    for (int i = 1; i <= cnt; i++) {
        for (int j = 1; j <= ss; j++) {
            long long t = v[j];
            if (prime[i] > t / prime[i]) break;
            g[getIdx(t)] -= g[getIdx(t / prime[i])] - i + 1;
        }
    }
    cout << S(n, 0, 0, 0) << endl;
    return 0;
}
  • i=1nj=1mf(ij),令积性函数f(n)满足f(pc)=pgcd(c,k),其中p为给定常数,p为素数,c为正整数。n105,m1010,k109

F(n,x)=i=1nf(ix),ans=i=1nF(m,i)仿min25ppxcF(n,x)=penf(pe)(F(npe,xpc)F(npe+1,xpc1))O(nm),x106107F(n,1)F(n,1)=i=1nf(i),fidf便PowerfulNumberf=idh,Sf(n)=dPNh(d)nd(nd+1)2,h(pc)=pgcd(c,k)t=1cpth(pct)

#include <assert.h>

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <vector>
#define int long long

using namespace std;

const int mo = 1e9 + 7;
const int inv2 = (mo + 1) >> 1;
const int M = 1e5 + 5;
const int N = 2e6 + 5;

int n, m, prime[200005], cnt = 0, vis[N + 5], k, tmp = 0, pns[1000005],
                         cs[N + 5];
vector<int> h[10005];
vector<pair<int, int>> pn;
vector<int> pp[100005];
map<int, int> mp[100005];

inline int gcd(int o, int p) { return (!p) ? o : gcd(p, o % p); }

inline int getIdx(int o) { return (o <= M) ? o : (M + m / o); }

inline int Mod(int o) { return (o >= mo) ? (o - mo) : o; }

int mi(int o, int p) {
    int yu = 1;
    while (p) {
        if (p & 1) yu = yu * o % mo;
        o = o * o % mo;
        p >>= 1;
    }
    return yu;
}

vector<array<int, 3>> factor(int o) {
    vector<array<int, 3>> ans;
    for (int i = 1; i <= cnt; i++) {
        if (o % prime[i] == 0) {
            int yu = 0;
            int tt = 1;
            while (o % prime[i] == 0) {
                yu++;
                tt *= prime[i];
                o /= prime[i];
            }
            ans.push_back(array<int, 3>{yu, prime[i], tt});
        }
        if (prime[i] * prime[i] >= o) break;
    }
    if (o > 1) {
        ans.push_back(array<int, 3>{1, o, o});
    }
    return ans;
}

void init() {
    memset(pns, -1, sizeof(pns));
    memset(cs, -1, sizeof(cs));
    for (int i = 2; i <= N; i++) {
        if (!vis[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt && i * prime[j] <= N; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
    for (int i = 1; i <= cnt; i++) {
        if (prime[i] > 100000) break;
        h[i].push_back(1);
        for (int j = prime[i], t = 1; j <= m; j *= prime[i], t++) {
            int sum = 0;
            for (int l = 0; l < t; l++) {
                sum = Mod(sum + mi(prime[i], t - l) * h[i][l] % mo);
            }
            h[i].push_back(Mod(mi(prime[i], gcd(t, k)) - sum + mo));
        }
    }
}

void pre(int o, int p, int q) {
    cs[o] = p % mo;
    for (int j = q; j <= cnt; j++) {
        if (o * prime[j] > N) break;
        for (int t = prime[j] * o, s = 1; t <= N; t *= prime[j], s++) {
            pre(t, p * mi(prime[j], gcd(s, k)) % mo, j + 1);
        }
    }
}

void getPN(int o, int p, int q) {
    pn.push_back(make_pair(o, p));
    for (int j = q; j <= cnt; j++) {
        if (prime[j] > 100000) break;
        if (prime[j] * prime[j] > m / o) break;
        for (int t = prime[j] * prime[j] * o, s = 2; t <= m;
             t *= prime[j], s++) {
            getPN(t, p * h[j][s] % mo, j + 1);
        }
    }
}

inline int s2(int o) { return o * (o + 1) % mo * inv2 % mo; }

int calH(int o) {
    int t = getIdx(o);
    if (pns[t] != -1) return pns[t];
    int ans = 0;
    for (auto [l, r] : pn) {
        if (l > o) break;
        ans = Mod(ans + r * s2(o / l % mo) % mo);
    }
    return pns[t] = ans;
}

int cal(int o, vector<array<int, 3>> p, int q) {
    if (o == 0) return 0;
    if (o == 1) return cs[q];
    if (p.empty()) return calH(o);
    if (o * q <= N) return pp[q][o];
    if (mp[q].find(o) != mp[q].end()) return mp[q][o];
    tmp++;
    int ans = 0;
    int q1 = q / p.back()[2];
    int q2 = q1 * p.back()[1];
    for (int i = 1, t = 0; i <= o; i *= p.back()[1], t++) {
        vector<array<int, 3>> p1 = p;
        vector<array<int, 3>> p2 = p;
        p1.pop_back();
        p2.pop_back();
        p2.push_back(array<int, 3>{1, p.back()[1], p.back()[1]});
        ans =
            (ans + mi(p.back()[1], gcd(k, t + p.back()[0])) *
                       (cal(o / i, p1, q1) - cal(o / i / p.back()[1], p2, q2)) %
                       mo) %
            mo;
        ans = Mod(ans + mo);
    }
    return mp[q][o] = ans;
}

signed main() {
    scanf("%lld%lld%lld", &n, &m, &k);
    init();
    pre(1, 1, 1);
    getPN(1, 1, 1);
    sort(pn.begin(), pn.end());
    for (int i = 1; i <= n; i++) {
        pp[i].push_back(0);
        for (int j = 1; i * j <= N; j++) {
            int yu = Mod(pp[i][pp[i].size() - 1] + cs[i * j]);
            pp[i].push_back(yu);
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans = (ans + cal(m, factor(i), i)) % mo;
    }
    cout << ans << endl;
    return 0;
}
posted @   clapp  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示