题解 P6021 【【模板】质数前缀统计】
command_block · · 题解
我们考虑借鉴埃式筛法的思想,逐渐使用质数将不合法的数筛去。
- 引理 : 合数
m 必有一个\sqrt{m} 以内的因子。
因此,实现埃式筛的时候,只需要利用
令
我们以
考虑每次减去被筛掉的数的贡献,可以得到递推式 :
边界 :
至于如何求解自然数幂和,不在本文的范围,可见 P4593 & CF622F , 使用拉格朗日插值这部分复杂度为
- 解释 :
我们必须不重不漏地选出最小素因子为
考虑埃式筛是怎么筛的 : 每次从已知的数范围内枚举
那么就能得到
不漏的性质显然。
这样子,我们去除
注意到
当
形式上就是 :
注意到我们在递推中需要利用
递推的运算次数
总的复杂度就是
这还不够优秀,并且由于常数问题,跑过暴力需要精细实现。
- ④
考虑省掉递推运算中的部分树状数组操作。
注意到
观察
此时,这个函数值仍在变化的条件是
也就是说,在
这部分总复杂度是
这时该函数值早已确定,前缀和即可。
这样子的总复杂度是
- ⑤
想要更快的话请见 : zzt巨神 :《一些特殊的数论函数求和问题》(2018国家候选队队论文)
里面提出了一种
- ⑥ 听说有
O(\frac{n^{2/3}}{logn}) 的做法。
这里只给出
使用了一个除法优化。
#include<algorithm>
#include<cstdio>
#include<cmath>
#define Limit 320000
typedef long long ll;
using namespace std;
int mod;
ll powM(ll a,int t)
{
ll ans=1;
while(t){
if (t&1)ans=ans*a%mod;
a=a*a%mod;
t>>=1;
}return ans;
}
int k,m;
ll lf[15],rf[15],ifac[15],y[15];
void Init()
{
m=k+2;
for (int i=1;i<=m;i++)
y[i]=(y[i-1]+powM(i,k))%mod;
ifac[0]=ifac[1]=1;
for (int i=2;i<=m;i++)
ifac[i]=ifac[mod%i]*(mod-mod/i)%mod;
for (int i=2;i<=m;i++)
ifac[i]=ifac[i]*ifac[i-1]%mod;
lf[0]=1;
}
ll gPows(ll N)
{
N%=mod;
for (int i=1;i<=m;i++)
lf[i]=lf[i-1]*(N-i)%mod;
rf[m+1]=1;
for (int i=m;i;i--)
rf[i]=rf[i+1]*(N-i)%mod;
ll ans=0;
for (int i=1;i<=m;i++)
ans=(ans+y[i]*lf[i-1]%mod*
rf[i+1]%mod*ifac[i-1]%mod*
((m-i)&1 ? mod-ifac[m-i] : ifac[m-i]))%mod;
return ans;
}
ll N,h0[Limit],h1[Limit];
int lim;
int main()
{
scanf("%lld%d%d",&N,&k,&mod);
Init();
lim=sqrtl(N);
for(int i=1;i<=lim;++i){
h1[i]=gPows(N/i)-1;
h0[i]=gPows(i)-1;
}
for(int i=2;i<=lim;++i){
h0[i]=(h0[i]+mod)%mod;
if(h0[i]==h0[i-1])continue;
ll x0=h0[i-1],r=(ll)i*i,p0=powM(i,k);
int u=min((ll)lim,N/((ll)i*i)),uu=min(u,lim/i);
for(int j=1;j<=uu;++j)
h1[j]=(h1[j]-p0*(h1[j*i]-x0))%mod;
ll t=N/i;
if (t<(1ll<<31))
for(int tt=t,j=uu+1;j<=u;++j)
h1[j]=(h1[j]-p0*(h0[tt/j]-x0))%mod;
else
for(int j=uu+1;j<=u;++j)
h1[j]=(h1[j]-p0*(h0[t/j]-x0))%mod;
for(int j=lim;j>=r;--j)
h0[j]=(h0[j]-p0*(h0[j/i]-x0))%mod;
}
ll ans=0;
for (int i=1;i<=lim;i++)
ans=(ans+h1[i]*i%mod*i)%mod;
printf("%lld\n",(ans+mod)%mod);
}