这些各种乱七八糟的筛法真难懂……
首先Min_25筛的基本思想就是在不停的枚举最小质因子。
zzt的论文根本看不懂。
下文所有除法为了方便都是下取整。
过程是这样的,我们先求这样一个东西:
g(n)=∑i=1n[i is a prime]F(i)g(n)=\sum_{i=1}^n [\mathrm{i\ is\ a\ prime}]F(i)g(n)=i=1∑n[i is a prime]F(i)
通常题目会保证F(p)F(p)F(p)是一个系数固定的低次多项式,假设是k次,那么我们就是要求:
gk(n)=∑i=1n[i is a prime]ikg_k(n)=\sum_{i=1}^n[\mathrm{i\ is\ a\ prime}]i^kgk(n)=i=1∑n[i is a prime]ik
这个怎么算,首先将g(n)g(n)g(n)初始化为∑i=1nik\sum_{i=1}^n i^k∑i=1nik,然后减去不合法的部分(即iii不是质数的情况)。
即我们从小到大枚举质数ppp,并且定义:
gk(n)=∑i=1n[i是质数或者i的最小质因子大于p]ikg_k(n)=\sum_{i=1}^n [i是质数或者i的最小质因子大于p]i^kgk(n)=i=1∑n[i是质数或者i的最小质因子大于p]ik
那么随着ppp的增大,gk(n)g_k(n)gk(n)会越来越小。每次会减小哪些呢?显然是那些以ppp做最小质因子的情况,也就是x=tpx=tpx=tp,其中ttt的最小质因子不小于ppp。这些xxx的和就是pkgk(n/p)p^k g_k(n/p)pkgk(n/p),但是这其中xxx还有可能是小于ppp的质数,需要减去,因此答案准确的说应当是pk(gk(n/p)−gk(p−1))p^k(g_k(n/p)-g_k(p-1))pk(gk(n/p)−gk(p−1))。这就是要从gk(n)g_k(n)gk(n)减去这个东西。
下文我们使用的时候,设s=⌊N⌋,x≤ss=\lfloor\sqrt N\rfloor,x\le ss=⌊N⌋,x≤s,要么我们要问gk(x)g_k(x)gk(x),要么问gk(N/x)g_k(N/x)gk(N/x)。(NNN和nnn是不一样的,NNN是输入的,全局固定的)。
我们可以注意到,若a1,a2≤sa_1,a_2\le sa1,a2≤s并且n/a1=n/a2n/a_1=n/a_2n/a1=n/a2,那么可以说明a1=a2a_1=a_2a1=a2。
因此我们用A(x)=gk(x),B(x)=gk(N/x),1≤x≤sA(x)=g_k(x),B(x)=g_k(N/x),1\le x\le sA(x)=gk(x),B(x)=gk(N/x),1≤x≤s。(代码里面是s和l)。
显然上文过程ttt是可能包含ppp作为最小质因子的,因此nnn要从大到小枚举,并且对于n<p2n<p^2n<p2的nnn是没有讨论必要的。
转移(注意转移顺序):
B(n)=gk(N/n),−=pk(gk(n/ip)−g(p−1)),n≤min(N/p2,s)∴when n≤s/p,B(n)−=pk(B(np)−A(p−1))otherwise B(n)−=pk(A(n/(ip))−A(p−1))A(n)=gk(n),−=pk(gk(n/p)−gk(p−1))=pk(A(n/p)−A(p−1))B(n)=g_k(N/n),-=p^k(g_k(n/ip)-g(p-1)),n\le \min (N/p^2,s) \\
\therefore \mathrm{when\ }n\le s/p,B(n)-=p^k(B(np)-A(p-1))\\
\mathrm{otherwise\ }B(n)-=p^k(A(n/(ip))-A(p-1))\\
A(n)=g_k(n),-=p^k(g_k(n/p)-g_k(p-1))=p^k(A(n/p)-A(p-1))B(n)=gk(N/n),−=pk(gk(n/ip)−g(p−1)),n≤min(N/p2,s)∴when n≤s/p,B(n)−=pk(B(np)−A(p−1))otherwise B(n)−=pk(A(n/(ip))−A(p−1))A(n)=gk(n),−=pk(gk(n/p)−gk(p−1))=pk(A(n/p)−A(p−1))
(上面虽然看上去好长其实很好推,之不过我把所有细节写下来了而已)
然后考虑维护答案。
首先记S(x,y)S(x,y)S(x,y)表示1~x1~x1~x中,最小质因子不小于prime[y]prime[y]prime[y]的数字的FFF的和。那么答案显然就是S(n,1)+F(1)S(n,1)+F(1)S(n,1)+F(1)。
考虑去计算S(x,y)S(x,y)S(x,y),答案分成两部分,
第一部分是iii是质数的情况,这个直接用刚刚求出的ggg函数做一下就可以了;
后半部分考虑枚举iii的最小质因子prime[z]>=prime[y]prime[z]>=prime[y]prime[z]>=prime[y],那么这个i=prime[z]e×ti=prime[z]^e\times ti=prime[z]e×t,其中ttt的最小质因子>prime[z]>prime[z]>prime[z](注意是大于)。
这样枚举zzz和e≥1e\ge1e≥1使得prime[z]e+1≤xprime[z]^{e+1}\le xprime[z]e+1≤x,然后答案加上S(x/(prime[z]e),z+1)×F(prime[z]e)+F(prime[z]e+1)S(x/(prime[z]^e),z+1)\times F(prime[z]^e)+F(prime[z]^{e+1})S(x/(prime[z]e),z+1)×F(prime[z]e)+F(prime[z]e+1)(后面那个是去计算t=1t=1t=1的情况,由于F(prime[z])F(prime[z])F(prime[z])已经在之前的ggg函数中处理过了,所以这里就不用算了)。
注意到这里至少是prime[z]2≤xprime[z]^2\le xprime[z]2≤x,因此只要枚举到根号xxx的质数即可。
这里一个小细节是为啥没有加上S(x/(prime[z]e+1),z+1)×F(prime[z]e+1)S(x/(prime[z]^{e+1}),z+1)\times F(prime[z]^{e+1})S(x/(prime[z]e+1),z+1)×F(prime[z]e+1)(eee取到最大),这是因为prime[z]prime[z]prime[z]的e+2e+2e+2次是超过了nnn的,那么prime[z]prime[z]prime[z]的e+1e+1e+1次再乘以后面某个大于prime[z]prime[z]prime[z]的质数也就爆炸了)。
……终于说完了。这东西复杂度为啥是对的我也不知道,反正跑的还挺快就是了。
代码是divcntk的代码,满足F(p)=k+1F(p)=k+1F(p)=k+1(是个零次多项式)。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ull unsigned long long
#define MXS 100010
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int notp[MXS],pri[MXS];
ull s[MXS],l[MXS];
inline int my_sqrt(ull n)
{
int x=(int)sqrt(n);
while(1ull*x*x<n) x++;
while(1ull*x*x>n) x--;
return x;
}
inline int prelude(int n)
{
notp[1]=1;int pc=0;
for(int i=2;i<=n;i++) if(!notp[i])
{
pri[++pc]=i;
for(int j=i;(ull)j*i<=(ull)n;j++)
notp[i*j]=1;
}
return pri[++pc]=n+1;
}
inline int get_g(ull n,int sn,ull k)
{
for(int i=1;i<=sn;i++) s[i]=i,l[i]=n/i;
for(int p=2;p<=sn;p++)
{
if(notp[p]) continue;ull r=(ull)p*p,v=s[p-1];
int ed1=sn/p,ed2=(int)min(n/r,(ull)sn);
for(int i=1;i<=ed1;i++) l[i]-=l[i*p]-v;
for(int i=ed1+1;i<=ed2;i++) l[i]-=s[n/i/p]-v;
for(int i=sn;(ull)i>=r;i--) s[i]-=s[i/p]-v;
}
for(int i=1;i<=sn;i++) s[i]*=(k+1),l[i]*=(k+1);
return 0;
}
inline ull F(ull n,ull x,int y,int sn,ull k)
{
if((ull)pri[y]>x) return 0;ull res=-s[pri[y]-1];
if(x<=(ull)sn) res+=s[x];else res+=l[n/x];
for(int i=y;(ull)pri[i]*pri[i]<=x;i++)
{
ull v=pri[i];
for(int j=1;v*pri[i]<=x;j++,v*=pri[i])
res+=F(n,x/v,i+1,sn,k)*(j*k+1)+(j+1)*k+1;
}
return res;
}
int main()
{
ull n,k;int sn;scanf("%llu%llu",&n,&k);
sn=my_sqrt(n),prelude(sn),get_g(n,sn,k);
return !printf("%llu\n",F(n,n,1,sn,k)+1);
}