UOJ188【UR #13】Sanrd (Min_25筛)

这里
题意:说白了就是求这个 ∑ i = 1 n p s m a x ( i ) \sum_{i=1}^{n}p_{smax}(i) i=1npsmax(i)
其中 p s m a x p_{smax} psmax表示次小质因子,规定1和质数的次小质因子为0
做法:这道题是一道min25筛法的题,但不过不是普通的积性函数求和,需要对该算法有一定的理解。
首先我们还是以 s ( n , j ) s(n,j) s(n,j)表示在 1 − n 1-n 1n范围内最小质因子不小于 p j p_j pj的和。
对于质数部分没有贡献为0,对于合数部分我们枚举最小质因子 p j p_j pj的幂次,那么合数分为两部分,一部分 p j k p l ( l > j ) , p j k x p_{j}^{k}p_{l}(l>j),p_{j}^{k}x pjkpl(l>j)pjkx,这两部分中前一部分的答案为 p j p_{j} pj,后一部分的答案 x x x这个合数的答案,因此 s ( n , j ) = ∑ k = j ∣ p ∣ ∑ e = 1 n ( s ( n / p j k , k + 1 ) + p k ∑ x = p k n i s p r i m e ) s(n,j) = \sum_{k=j}^{|p|}\sum_{e=1}^{n} (s(n/p_{j}^{k},k+1)+p_{k}\sum_{x=p_{k}}^{n}isprime) s(n,j)=k=jpe=1n(s(n/pjk,k+1)+pkx=pknisprime)
后面关于质数的计数部分,直接 g g g函数的处理即可。
听它们说还可以有另外一种写法就是强制第y层的质数为最大质因子,这样递推,我也没有这样写过,但不过估计比这个复杂。

#include "bits/stdc++.h"

using namespace std;
inline int read() {
    int x = 0;
    bool f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = 0;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    if (f) return x;
    return 0 - x;
}
typedef long long ll;
const int maxn = 400010;
const ll mod = 1000000000 + 7;
ll quick(ll a, ll n, ll p) {
    ll ans = 1;
    for (; n; n >>= 1, a = a * a % p)
        if (n & 1) ans = ans * a % p;
    return ans;
}
int vis[maxn], sqr, cnt = 0;
ll suf1[maxn], suf2[maxn], inv2, inv6, n, pri[maxn];
void init(int lim) {
    vis[1] = 1;
    for (int i = 2; i <= lim; i++) {
        if (!vis[i]) {
            pri[++cnt] = i;
        }
        for (int j = 1; j <= cnt && i * pri[j] <= lim; j++) {
            vis[i * pri[j]] = 1;
            if (i % pri[j] == 0) break;
        }
    }
}
int id1[maxn], id2[maxn], m = 0;
ll w[maxn << 1], g[maxn << 1], h[maxn << 1];
int getid(ll x) {
    if (x <= sqr) return id1[x];
    return id2[n / x];
}

void calc1() {
    for (ll l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l);
        w[++m] = n / l;
        g[m] = w[m] - 1;
        if (w[m] <= sqr) id1[w[m]] = m;
        else id2[r] = m;
    }
    for (int j = 1; j <= cnt; j++) {
        for (int i = 1; i <= m && pri[j] * pri[j] <= w[i]; i++) {
            int k = getid(w[i] / pri[j]);
            g[i] -= g[k] - j + 1;
        }
    }
}

ll sum(ll x, int y) {
    if (x <= 1 || pri[y] > x) return 0;
    int k = getid(x);
    ll ret = 0;
    for (int i = y; i <= cnt && 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 += (sum(x / t1, i + 1) + 1ll * pri[i] * (g[getid(x / t1)] - i + 1));
        }
    }
    return ret;
}

int main() {
//    inv2 = quick(2, mod - 2, mod);
//    inv6 = quick(6, mod - 2, mod);
    ll l, r;
    cin >> l >> r;
    n = r;
    sqr = sqrt(n);
    init(sqr);
    calc1();
    ll ans = sum(n, 1);

    n = l - 1;
    sqr = sqrt(n);
    m = 0;
    calc1();
    ans = ans - sum(n, 1);
    cout << ans << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值