original link - https://nanti.jisuanke.com/t/41390
题意:
解析:
∑ i = 1 n f ( i ! ) = ( n + 1 ) ∑ i = 1 n f ( i ) − ∑ i = 1 n i f ( i ) \sum_{i=1}^nf(i!)=(n+1)\sum_{i=1}^nf(i)-\sum_{i=1}^nif(i) i=1∑nf(i!)=(n+1)i=1∑nf(i)−i=1∑nif(i)
考虑每个 p e p^e pe的贡献,前半部分中 ∑ i = 1 n f ( i ) = ∑ ⌊ n p e ⌋ \sum_{i=1}^nf(i)=\sum \lfloor\frac{n}{p^e}\rfloor ∑i=1nf(i)=∑⌊pen⌋,例如 [ 1 , 10 ] [1,10] [1,10]中 2 2 2的贡献为 5 5 5, 4 4 4的贡献为 2 2 2, 8 8 8的贡献为 1 1 1…\
但是我们只能枚举 p ≤ n p\leq\sqrt n p≤n的质数,想办法处理 p ≥ n p\geq \sqrt n p≥n的部分。 p ≥ n p\geq \sqrt n p≥n的部分 e e e只能是 1 1 1,所以变为 ∑ p ≥ n ⌊ n p ⌋ \sum_{p\geq \sqrt n}\lfloor\frac{n}{p}\rfloor ∑p≥n⌊pn⌋。方便起见,我们枚举质数时从 e = 2 e=2 e=2开始算,另外部分变为 ∑ p ∈ [ 1 , n ] ⌊ n p ⌋ \sum_{p\in[1,n]}\lfloor\frac{n}{p}\rfloor ∑p∈[1,n]⌊pn⌋。
这个式子可以分块去做,假设 p ∈ [ l , r ] p\in[l,r] p∈[l,r]时 ⌊ n p ⌋ = x \lfloor\frac{n}{p}\rfloor=x ⌊pn⌋=x,这部分答案就是 [ l , r ] [l,r] [l,r]的质数个数乘上 x x x。怎么做区间的质数个数? m i n _ 25 min\_25 min_25筛即可。
还有就是 ∑ i = 1 n i f ( i ) \sum_{i=1}^nif(i) ∑i=1nif(i),想一下式子为 ∑ i = 1 n i f ( i ) = ∑ ⌊ n p e ⌋ ∗ ( ⌊ n p e ⌋ + 1 ) 2 p e \sum_{i=1}^nif(i)=\sum \dfrac{\lfloor\frac{n}{p^e}\rfloor*(\lfloor\frac{n}{p^e}\rfloor+1)}{2}p^e ∑i=1nif(i)=∑2⌊pen⌋∗(⌊pen⌋+1)pe。
同样,先枚举 p ≤ n p\leq\sqrt n p≤n的质数的 e > 1 e>1 e>1次方,另外部分为 ∑ p ∈ [ 1 , n ] ⌊ n p ⌋ ∗ ( ⌊ n p ⌋ + 1 ) 2 p \sum_{p\in[1,n]}\dfrac{\lfloor\frac{n}{p}\rfloor*(\lfloor\frac{n}{p}\rfloor+1)}{2}p ∑p∈[1,n]2⌊pn⌋∗(⌊pn⌋+1)p, ⌊ n p ⌋ \lfloor\frac{n}{p}\rfloor ⌊pn⌋分块处理,剩下部分为区间内的质数之和,也是 m i n _ 25 min\_25 min_25筛。
代码:
/*
* Author : Jk_Chen
* Date : 2019-09-27-11.16.23
*/
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
#define debug(x) cerr<<#x<<" = "<<x<<'\n';
const LL mod=998244353;
const int maxn=1e6+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________begin*/
LL Pow(LL a,LL b,LL mod){
if(a>=mod)a%=mod;
LL res=1;
while(b>0){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
/*_________________________________________________________Pow*/
void Add(LL &a,LL b){a=(a%mod+b%mod);while(a>mod)a-=mod; while(a<0)a+=mod;}
LL inv2=Pow(2,mod-2,mod),inv3=Pow(3,mod-2,mod);
bool vis[maxn];
LL pri[maxn],now;
LL sum[maxn];
void init(int n){// 素数筛
rep(i,2,n){
if(!vis[i])pri[++now]=i,sum[now]=(sum[now-1]+i)%mod;
for(int j=1;j<=now&&1ll*pri[j]*i<=n;j++){
vis[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
}
LL n,sqr;
LL val[maxn],idx1[maxn],idx2[maxn],ct;// 离散化
LL g0[maxn],g1[maxn];// g函数值
int main(){
n=rd();
sqr=sqrt(1.0*n);
init(sqr);
// n/i 进行分段
for(LL l=1,r;l<=n;l=r+1){
r=n/(n/l);
val[++ct]=n/l;
n/l<=sqr ? idx1[n/l]=ct : idx2[n/(n/l)]=ct;
// 初始化g(x,0) x=val[ct]
// f(p)=1 , 1+1+...1
g0[ct]=val[ct]-1;
// f(p)=i , 2+3+...val[ct]
g1[ct]=(2+val[ct])%mod*((val[ct]-1)%mod)%mod*inv2%mod; // 注意1e10*1e10
}
// 预处理 g(n/x,|P|) |P|=now
rep(j,1,now){ //g(val[i],j)
rep(i,1,ct){
if(pri[j]*pri[j]>val[i])break; // g(x,j)=g(x,j-1)
int k = // idx(val[i]/Pj)
val[i]/pri[j]<=sqr ? idx1[val[i]/pri[j]] : idx2[n/(val[i]/pri[j])];
Add(g0[i],-(g0[k]-(j-1)));
Add(g1[i],-pri[j]*((g1[k]-sum[j-1])%mod)%mod);
}
}
LL ans=0;
rep(i,1,now){
LL v=pri[i]*pri[i];
while(v<=n){
Add(ans,(n+1)%mod*(n/v%mod)%mod);
Add(ans,-(n/v+1)%mod*(n/v%mod)%mod*inv2%mod*(v%mod)%mod);
v*=pri[i];
}
}
for(LL l=1,r;l<=n;l=r+1){
r=n/(n/l);
int R= r<=sqr?idx1[r]:idx2[n/r], L= l-1<=sqr?idx1[l-1]:idx2[n/(l-1)];
Add(ans, (n/l)%mod*((n+1)%mod)%mod*((g0[R]-g0[L])%mod)%mod);
Add(ans, -(n/l+1)%mod*(n/l%mod)%mod*inv2%mod*((g1[R]-g1[L])%mod)%mod);
}
printf("%lld\n",ans);
return 0;
}