P5493 质数前缀统计 题解
前言 借此篇题解记录一下 Min25 筛中的预处理所有 Sleft(leftlfloor frac{n}{i}rightrfloorright) 方法。 记号与约定 在本文中若无特殊说明,记号均采用以…
前言
借此篇题解记录一下 Min_25 筛中的预处理所有 方法。
记号与约定
在本文中若无特殊说明,记号均采用以下定义:
-
。
-
表示一个质数。
-
表示第 个质数,令 。
-
表示 的最小质因子。
-
表示 是否是质数。
-
题面中的 在本文中记作 。同时 失去原有定义。
正文
考虑直接求得 并不好求,于是我们考虑设计一种递推的方法,从全体小于 的自然数开始,依次去掉最小质因子为 的合数,这样做的好处非常明显,因为对于一个合数 有 ,所以我们令 为最大的 ,那么 ,意思是只需要递推到 即可,并且 并不大可以粗略看作 的。定义 :
自然有边界 为了预处理这个边界可以参考 CF622F 一题,可以在 内处理出单点值。
现在考虑从 递推到 ,考虑这个过程中哪些数被踢了出去,自然是最小质因子为 的合数,把 提出来可以得到: 但是这样把小于 中小于 质数会成为更小的质因子,所以是 ,最终递推式子:
虽然 只有 ,但是第一维似乎还是很大?别急理性分析一波,第一维的形式一定类似于 一直连除一些数向下取整的形式,由于有:
所以,第一维只可能是 中的取值,很经典地,我们知道这个东西取值只有 个,但是这两个上界都很不紧,继续通过一些列分析可以得到最终递推复杂度是 的,具体过程可以参考 OI Wiki 上的证明。
代码
#include<bits/stdc++.h>
// #define ONLINE_JUDGE
#define INPUT_DATA_TYPE long long
#define OUTPUT_DATA_TYPE int
INPUT_DATA_TYPE read(){register INPUT_DATA_TYPE x=0;register char f=0,c=getchar();while(c<'0'||'9'<c)f=(c=='-'),c=getchar();while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();return f?-x:x;}void print(OUTPUT_DATA_TYPE x){if(x<0)x=-x,putchar('-');if(x>9)print(x/10);putchar(x%10^48);return;}
long long mod;
int K;
long long qpow(register long long base,register long long e){
register long long res=1;
base%=mod;
while(e){
if(e&1) res=(res*base)%(mod);
base=(base*base)%(mod);
e>>=1;
}
return res;
}
#define INV_DATA_TYPE long long
INV_DATA_TYPE exgcd(INV_DATA_TYPE a,INV_DATA_TYPE b,INV_DATA_TYPE &x,INV_DATA_TYPE &y){
if(!b){
x=1;
y=0;
return a;
}
INV_DATA_TYPE d=exgcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
INV_DATA_TYPE inv(INV_DATA_TYPE n,INV_DATA_TYPE p){
n%=p;
INV_DATA_TYPE x,y;
exgcd(n,p,x,y);
x%=p;
return x>=0?x:x+p;
}
long long pl[20],pr[20],fac[20],y[20],b[20];
long long S_all(long long n){
register int i;
register long long ans=0,a;
pl[0]=pr[K+3]=1;
for(i=1;i<=K+2;i++) pl[i]=pl[i-1]*(n%mod-i)%mod;
for(i=K+2;i>=1;i--) pr[i]=pr[i+1]*(n%mod-i)%mod;
for(i=1;i<=K+2;i++){
a=pl[i-1]*pr[i+1]%mod;
(ans+=y[i]*a%mod*b[i]%mod)%=mod;
}
return (ans+mod)%mod;
}
long long sp[1000010],n;
long long g[1000010],val[1000010];
int sqrtN,cntId,idx_s[1000010],idx_b[1000010];
std::bitset<1000010> isPrime;
long long prime[1000010];
int cntPri;
void Euler(int n){
register long long i,j;
for(i=2;i<=n;++i){
if(!isPrime[i]){
prime[++cntPri]=i;
sp[cntPri]=(sp[cntPri-1]+qpow(i,K))%mod;
}
for(j=1;j<=cntPri&&prime[j]*i<=n;++j){
isPrime[prime[j]*i]=1;
if(!(i%prime[j])) break;
}
}
}
int getIdx(long long a){
if(a<=sqrtN) return idx_s[a];
else return idx_b[n/a];
}
void initG(){
register long long i,nxt,tmp;
register int id;
for(i=1;i<=n;i=nxt+1){
nxt=n/(n/i);
val[++cntId]=n/i;
g[cntId]=(S_all(val[cntId])+mod-1)%mod;
if(n/i<=sqrtN) idx_s[n/i]=cntId;
else idx_b[n/(n/i)]=cntId;
}
for(register int i=1,j;i<=cntPri;++i){
tmp=qpow(prime[i],K);
for(j=1;j<=cntId&&prime[i]*prime[i]<=val[j];++j){
id=getIdx(val[j]/prime[i]);
(g[j]+=mod-tmp*(g[id]+mod-sp[i-1])%mod)%=mod;
}
}
return;
}
long long inv6;
long long s2(long long n){
n%=mod;
return n*(n+1)%mod*(n*2+1)%mod*inv6%mod;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("name.in", "r", stdin);
freopen("name.out", "w", stdout);
#endif
register long long res=0,i,nxt;
sqrtN=std::sqrt(n=read());
K=read();
mod=read();
for(i=fac[0]=1;i<=K+2;i++) fac[i]=fac[i-1]*i%mod,y[i]=(y[i-1]+qpow(i,K))%mod;
for(i=1;i<=K+2;i++) b[i]=inv(fac[i-1]*((K-i)&1?mod-1:1)%mod*fac[K+2-i],mod)%mod;
inv6=inv(6,mod);
Euler(sqrtN);
initG();
for(i=1;i<=sqrtN;i=nxt+1){
nxt=std::min(n/(n/i),(long long)sqrtN);
(res+=(s2(nxt)+mod-s2(i-1))*g[getIdx(n/i)])%=mod;
}
print(res);
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}谢谢你,读到这里。
本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗