2019徐州网络赛 H.function

题意:
先有\(n=p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m}\),定义\(f(n)=k_1+k_2+\cdots+k_m\)
现在计算
\[ \sum_{i=1}^nf(i!)\% 998244353 \]

思路:
首先注意到\(f\)函数有这样一个性质:\(f(ab)=f(a)+f(b)\)
那么我们化简所求式子有:
\[ \begin{aligned} &\sum_{i=1}^nf(i!)\\ =&\sum_{i=1}^n\sum_{j=1}^if(j)\\ =&\sum_{i=1}^n (n-i+1)f(i)\\ =&(n+1)\sum_{i=1}^nf(i)-\sum_{i=1}^n if(i)\\ \end{aligned} \]

注意\(f\)并不是积性函数,但是我们根据上面的性质,发现\(\sum_{i=1}^nf(i)\)其实求的就是\(1,2,\cdots,n\)中,每个数的质因子指数和。就和对\(n!\)做质因子分解一样,我们只需要依次考虑每个素数的贡献,那么就可以化为:\((n+1)\sum_{i=1}^n[i\in P]\sum_{k=1}^{34}\lfloor\frac{n}{i^k}\rfloor\)
那后半部分呢?
还是像上面一样,每个质数依次考虑。假设对于质数\(p\)而言,那么所有有贡献的就是\(p,2\cdot p,\cdots,\lfloor\frac{n}{p}\rfloor \cdot p\),每个\(f\)的贡献为\(1\),那么答案就是\((1+2+\cdots+\lfloor\frac{n}{p}\rfloor)p\);对于\(p^2\)而言,每个\(f\)的贡献为\(2\),但是之前在\(p\)的时候已经算上一次,所以贡献就为\(1\)了,那么结果就和上面的差不多。

总结一下,最后推得的式子就为:

\[ (n+1)\sum_{i=1}^n[i\in P]\sum_{k=1}^{34}\lfloor\frac{n}{i^k}\rfloor-\sum_{i=1}^n[i\in P]\sum_{k=1}^{34}\frac{\lfloor\frac{n}{i^k}\rfloor(\lfloor\frac{n}{i^k}\rfloor+1)}{2}i^k \]

发现当\(k>1\)的时候很好处理,直接暴力算就行,照着上面式子写就行。
\(k=1\)的时候,因为是求每个素数的和,所以可以直接用\(min25\)筛的方法来搞。
细节详见代码吧,感觉也没啥细节,会\(min25\)就行。(然而我把线性筛写错没发现,调了一上午...)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, MOD = 998244353, inv = 499122177;

ll n, z;

bool chk[N];
int prime[N], tot;
ll p[N];
void pre() {
    for(int i = 2; i <= z; i++) {
        if(!chk[i]) {
            prime[++tot] = i;
            p[tot] = (p[tot - 1] + i) % MOD;
        }
        for(int j = 1; j <= tot && 1ll * i * prime[j] <= z; j++) {
            chk[i * prime[j]] = 1;
            if(i % prime[j] == 0) break;
        }
    }
}

ll w[N], g1[N], g2[N];
int ind[N], ind2[N];
int cnt;
void calc_g() {
    for(ll i = 1, j; i <= n; i = j + 1) {
        j = n / (n / i);
        w[++cnt] = n / i;
        if(w[cnt] <= z) ind[w[cnt]] = cnt;
        else ind2[n / w[cnt]] = cnt;
        g1[cnt] = (w[cnt] - 1) % MOD;
        g2[cnt] = w[cnt] % MOD * ((w[cnt] + 1) % MOD) % MOD * inv % MOD - 1;
    }
    for(int i = 1; i <= tot; i++) {
        for(int j = 1; j <= cnt && 1ll * prime[i] * prime[i] <= w[j]; j++) {
            ll tmp = w[j] / prime[i], k;
            if(tmp <= z) k = ind[tmp]; else k = ind2[n / tmp];
            g1[j] -= (g1[k] - i + 1) % MOD;
            g2[j] -= 1ll * (p[i] - p[i - 1]) * (g2[k] - p[i - 1]) % MOD;
            g1[j] %= MOD; g2[j] %= MOD;
            if(g1[j] < 0) g1[j] += MOD;
            if(g2[j] < 0) g2[j] += MOD;
        }
    }
}

ll work() {
    ll ans = 0;
    for(ll i = 1, j; i <= n; i = j + 1) {
        j = n / (n / i);
        ll l = ((i - 1 <= z) ? ind[i - 1] : ind2[(n / (i - 1))]);
        ll r = ((j <= z) ? ind[j] : ind2[n / j]);
        ans += (n / i) % MOD * ((n + 1) % MOD) % MOD * (g1[r] - g1[l]) % MOD;
        ans -= (n / i) % MOD * ((n / i + 1) % MOD) % MOD * inv % MOD * (g2[r] - g2[l]) % MOD;
        ans = (ans % MOD + MOD) % MOD;
    }
    for(int i = 1; i <= tot; i++) {
        ll prim = prime[i];
        for(; prim * prime[i] <= n;) {
            prim *= prime[i];
            ans += (n + 1) % MOD * ((n / prim) % MOD) % MOD;
            ans %= MOD;
            ans -= (n / prim) % MOD * (n / prim + 1) % MOD * inv % MOD * prim % MOD;
            ans %= MOD;
        }
    }
    if(ans < 0) ans += MOD;
    return ans;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n; z = sqrt(n) + 1;
    pre();
    calc_g();
    cout << work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/heyuhhh/p/11517015.html