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;
}

原文地址:https://www.cnblogs.com/heyuhhh/p/11517015.html

时间: 2024-11-07 10:48:33

2019徐州网络赛 H.function的相关文章

2019徐州网络赛 XKC&#39;s basketball team 线段树

网址:https://nanti.jisuanke.com/t/41387 题意: 大家好,我是训练时长两年半的个人练习生蔡徐坤,我的爱好是唱,跳,rap,篮球. 给出一段长度为$n,(n \leq 1e5)$的序列,对每一个数,求出它和它后面比它大$m$的数中间夹着的数的数量,没有输出$-1$. 题解: 直接建线段树,维护最大值,然后查询时对第$i$个数,搜索区间$[i,n]$之中大于$num[i]+m$的值的位置的最大值,具体操作是先限定区间,然后求出所有合法位置,取最大值,如果搜索不到则返

2018徐州网络赛H. Ryuji doesn&#39;t want to study

题目链接: https://nanti.jisuanke.com/t/31458 题解: 建立两个树状数组,第一个是,a[1]*n+a[2]*(n-1)....+a[n]*1;第二个是正常的a[1],a[2],a[3]...a[n] #include "bits/stdc++.h" using namespace std; #define ll long long const int MAXN=1e5+10; ll sum[MAXN],ans[MAXN]; ll num[MAXN];

ACM-ICPC 2018徐州网络赛-H题 Ryuji doesn&#39;t want to study

C*M....死于update的一个long long写成int了 心累 不想写过程了 ******** 树状数组,一个平的一个斜着的,怎么斜都行 题库链接:https://nanti.jisuanke.com/t/31460 #include <iostream> #include <cstring> #define ll long long #define lowbit(x) (x & -x) using namespace std; const int maxn =

2018 徐州网络赛 H

Ryuji is not a good student, and he doesn't want to study. But there are n books he should learn, each book has its knowledge a[i]a[i]. Unfortunately, the longer he learns, the fewer he gets. That means, if he reads books from ll to rr, he will get a

【2019.09.07】2019徐州网络赛

补题地址:https://www.jisuanke.com/contest/3005?view=challenges 题目: A:? B:? C:? D:? E:? F: G:? 回文树+二进制统计回文串内不同字母数技巧https://blog.csdn.net/Cassie_zkq/article/details/100606270 H: I:? J: K:? L: M:? 原文地址:https://www.cnblogs.com/ncu2019/p/11482910.html

2019徐州网络赛

I 题 query 题目链接 题目大意是给一个N(<=1e5) permutation  p (下标从1开始) , 现在定义一种pair( i ,j) ,其满足 min(pi?,pj?)=gcd(pi?,pj?),现在有M (<=1e5) 组区间查询[l,r]询问 满足 l <= i < j <= r 的pair有多少对. 这是一种偏序关系的问题,看了一点CDQ 解偏序的介绍 , 感觉也可以树状数组来搞.预处理出每个符合要求的点对( l , r )位置,在一维树状数组上修改点

query 2019徐州网络赛(树状数组)

query \[ Time Limit: 2000 ms \quad Memory Limit: 262144 kB \] 题意 补题才发现比赛的时候读了一个假题意.... 给出长度为 \(n\) 的排列,在给出 \(m\) 次询问,每次询问有一对 \(l.r\),问满足 \(min(ai, aj) = gcd(ai, aj)\) 的 \(pair(i, j)\) 对数. 思路 考虑离线做 先把每个数出现的位置记录下来,然后预处理出所有的 \(pair\). 对于一个数字 \(x\),其满足条件

2019徐州网络赛 I J M

I. query 比赛时候没有预处理因子疯狂t,其实预处理出来因子是\(O(nlog(n))\)级别的 每个数和他的因子是一对偏序关系,因此询问转化为(l,r)区间每个数的因子在区间(l,r)的个数 预处理出来每个位置上的数所有因子的位置,用可持久化线段树维护,区间询问 #include<bits/stdc++.h> #define ll long long #define mk make_pair #define ft first #define se second #define pii

2019 徐州网络赛 center

题意:n个点,求最小加上几个点让所有点关于一个点(不需要是点集里面的点)中心对称 题解:双重循环枚举,把中点记录一下,结果是n-最大的中点 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef unsigned long long ull; 5 #define mem(s) memset(s, 0, sizeof(s)) 6 const int INF = 0x3f3f3f3f;