51nod 1847 奇怪的数学题(Mobius反演+min_25筛+杜教筛+自然数幂和)

传送门.

题解:

s(n) s ( n ) 表示 nn n n 的 最 小 质 因 子

先小反演一下:

ni=1nj=1sgcd(i,j)k ∑ i = 1 n ∑ j = 1 n s g c d ( i , j ) k
=nd=2s(d)kni=1nj=1[gcd(i,j)=d] = ∑ d = 2 n s ( d ) k ∗ ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = d ]
=nd=2s(d)kndi=1ndj=1[gcd(i,j)=1] = ∑ d = 2 n s ( d ) k ∗ ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ [ g c d ( i , j ) = 1 ]
=nd=2s(d)k(2(ndi=1ϕ(i))1) = ∑ d = 2 n s ( d ) k ∗ ( 2 ( ∑ i = 1 ⌊ n d ⌋ ϕ ( i ) ) − 1 )

s s 的前缀和的筛法参考我的前一篇博客.

不过这里有个k次幂,于是初值就需要自然数幂和,模数 232 2 32 ,所以用第二类斯特林数,参考我的另一篇博客.

这个复杂度是 O(nk2) O ( n ∗ k 2 ) 的,用线性筛法预处理一些前多少的幂和会快很多。

ϕ ϕ 的前缀和的话可以也用min_25筛,不过此时写杜教筛显然是最好的选择,因为快,且对 ni ⌊ n i ⌋ 已经有了标号,也不用写hash表。

Code:

#include
#include
#define ll long long
#define ul unsigned int
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int N = 1e6 + 5;

int n, k, sqr, m;
int bz[N]; ul p[N], phi[N];
int w[N], i1[N], i2[N];
ul s[51][51], g[N], sk[N], sp[N], sum[N], h[N], c[N];

ul ksm(ul x, ul y) {
    ul s = 1;
    for(; y; y /= 2, x = x * x)
        if(y & 1) s = s * x;
    return s;
}

void sieve(int n) {
    fo(i, 2, n) bz[i] = 0; p[0] = 0;
    fo(i, 2, n) {
        if(!bz[i]) p[++ p[0]] = i, phi[i] = i - 1, sk[p[0]] = ksm(i, k), sp[p[0]] = sp[p[0] - 1] + sk[p[0]], c[i] = ksm(i, k);
        for(int j = 1; i * p[j] <= n; j ++) {
            int k = i * p[j]; bz[k] = 1;
            c[k] = c[i] * c[p[j]];
            if(i % p[j] == 0) { phi[k] = phi[i] * p[j]; break;}
            phi[k] = phi[i] * phi[p[j]];
        }
    }
    phi[1] = c[1] = 1;
    fo(i, 2, n) phi[i] += phi[i - 1], c[i] += c[i - 1];
}

ul zm(int n) {
    if(n <= 1e6) return c[n];
    ul S = 0;
    fo(j, 1, k) {
        ul p = 1;
        fo(i, n - j + 1, n + 1)
            p *= (i % (j + 1) == 0 ? i / (j + 1) : i);
        S += s[k][j] * p;
    }
    return S;
}

int bx[N]; ul bs[N];

ul dg(int x) {
    if(x <= 1e6) return phi[x];
    int k = x <= sqr ? i1[x] : i2[n / x];
    if(bx[k]) return bs[k];
    bx[k] = 1; bs[k] = (ul) x * (x + 1) / 2;
    for(int i = 2, j; i <= x; i = j + 1) {
        j = x / (x / i);
        bs[k] -= dg(x / i) * (j - i + 1);
    }
    return bs[k];
}

ul ans;

int main() {
    scanf("%d %d", &n, &k); sqr = sqrt(n);
    sieve(N - 5);
    s[0][0] = 1; fo(i, 1, k) fo(j, 1, i) s[i][j] = s[i - 1][j - 1] + s[i - 1][j] * j;
    for(int i = 1, j; i <= n; i = j + 1) {
        j = n / (n / i); w[++ m] = n / i;
        if(w[m] <= sqr) i1[w[m]] = m; else i2[n / w[m]] = m;
        h[m] = w[m] - 1;
        g[m] = zm(w[m]) - 1;
    }
    p[0] = 0; while(p[p[0] + 1] <= sqr) p[0] ++;
    fo(j, 1, p[0]) for(int i = 1; i <= m && p[j] * p[j] <= w[i]; i ++) {
        int k = (w[i] / p[j] <= sqr) ? i1[w[i] / p[j]] : i2[n / (w[i] / p[j])];
        g[i] -= sk[j] * (g[k] - sp[j - 1]);
        h[i] -= h[k] - j + 1;
        sum[i] += g[k] - sp[j - 1];
    }
    fo(i, 1, m) sum[i] += h[i];
    fo(i, 1, m) ans += (sum[i] - sum[i + 1]) * (dg(n / w[i]) * 2 - 1);
    printf("%u", ans);
}

你可能感兴趣的:(莫比乌斯反演,筛,数论杂集)