1. 程式人生 > >SPOJ divcntk(min25篩)

SPOJ divcntk(min25篩)

-- printf urn 素數 pan freopen pmo display style

題意

\(\sigma_0(i)\) 表示 \(i\) 的約數個數


\[ S_k(n)=\sum_{i=1}^n\sigma_0(i^k)\pmod {2^{64}} \]
\(T\) 組數據 \(T\le10^4,n,k\le10^{10}\)

題解

其實 SPOJ 上還有 divcnt2,divcnt3 ,三倍經驗題2333

其實是 min_25 裸題 233

\(f(x) = \sigma_0(x^k)\) ,不難發現它是個積性函數,且單點求值較快。

前面 講過了如何非遞歸在 \(\displaystyle O(\frac{n^{\frac{3}{4}}}{\ln n})\) 的時間裏處理出所有素數的積性函數的前綴和。

現在終於來填合數的部分了 qwq

\(S(n, i)\)\(\le n\) 的所有數 \(x\) 中,\(x\) 的最小質因子 \(\ge P_i\)\(f(x)\) 之和。

接下來我們先算上所有滿足條件的質數貢獻之和,即?\(\displaystyle g(n,|P|) - \sum_{j = 1}^{i - 1}f(P_j)\)?。

對於合數,我們利用積性的性質,直接枚舉其最小質因子以及質因子的個數,直接遞歸計算。

註意在這裏形如?\(p^k,(p∈P)\)?的貢獻並沒有算進去,所以還要單獨加一下。式子的形式如下:
\[ S(n,i)=g(n,|P|)-\sum_{j=1}^{i-1}f(P_j)+\sum_{k\ge i}^{|P|}\sum_{e}(f(P_k^e)S(\lfloor\frac{n}{P_k^e}\rfloor,k+1)+f(P_{k}^{e+1})) \]


最後我們需要求的就是 \(S(n, 1)\) 。這似乎對於非遞歸來說不太友好,我們遞歸計算。

如果當前的 \(n \le 1\) 或者 \(P_i > n\) 那麽直接返回 \(0\) 退出。(註意 \(1\) 的貢獻是最後單獨算的)

然後這個直接計算的復雜度似乎也是 \(\displaystyle O(\frac{n^{\frac{3}{4}}}{\ln n})\) 的。(不會證。。)

最後要解決這題的話,只需要知道

對於 \(x\) 的唯一分解 \(x = \prod_{i} {p_i}^{{k_i}}\)
\[ \sigma_0(x) = \prod_{i}(k_i + 1) \]

所以就有 \(f(p) = k + 1, f(p^e) = ek + 1\) 。然後就能解決此題啦。

代碼

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

typedef unsigned long long ll;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline ll read() {
    ll x(0), sgn(1); char ch(getchar());
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    return x * sgn;
}

void File() {
#ifdef zjp_shadow
    freopen ("34096.in", "r", stdin);
    freopen ("34096.out", "w", stdout);
#endif
}

const int N = 1e5 + 1e3;

int prime[N], pcnt; bitset<N> is_prime;

void Linear_Sieve(int maxn) {
    is_prime.set();
    For (i, 2, maxn) {
        if (is_prime[i]) prime[++ pcnt] = i;
        for (int j = 1; j <= pcnt && 1ll * i * prime[j] <= maxn; ++ j) {
            is_prime[i * prime[j]] = false; if (!(i % prime[j])) break;
        }
    }
}

int id1[N], id2[N]; ll val[N * 2], res[N * 2], k, d, all;

#define id(x) (x <= d ? id1[x] : id2[all / (x)])

void Min25_Sieve(ll n) {
    d = sqrt(n); int cnt = 0;
    for (ll i = 1; i <= n; i = n / (n / i) + 1)
        val[id(n / i) = ++ cnt] = n / i, res[cnt] = val[cnt] - 1;

    for (int i = 1; i <= pcnt && 1ll * prime[i] * prime[i] <= n; ++ i)
        for (int j = 1; j <= cnt && 1ll * prime[i] * prime[i] <= val[j]; ++ j)
            res[j] -= res[id(val[j] / prime[i])] - (i - 1);
}

ll S(ll n, int cur) {
    if (n <= 1 || (ll)prime[cur] > n) return 0;
    ll ans = (k + 1) * (res[id(n)] - (cur - 1));
    for (int i = cur; i <= pcnt && 1ll * prime[i] * prime[i] <= n; ++ i) {
        ll prod = prime[i];
        for (int e = 1; prod * prime[i] <= n; ++ e, prod *= prime[i])
            ans += (e * k + 1) * S(n / prod, i + 1) + ((e + 1) * k + 1);
    }
    return ans;
}

int main () {

    File();

    int cases = read();

    Linear_Sieve(1e5);

    while (cases --) {

        ll n = read(); k = read(); all = n;

        Min25_Sieve(n);

        printf ("%llu\n", S(n, 1) + 1);

    }

    return 0;

}

SPOJ divcntk(min25篩)