这些各种乱七八糟的筛法真难懂……
首先Min_25筛的基本思想就是在不停的枚举最小质因子。
zzt的论文根本看不懂。
下文所有除法为了方便都是下取整。
过程是这样的,我们先求这样一个东西:
g
(
n
)
=
∑
i
=
1
n
[
i
i
s
a
p
r
i
m
e
]
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次,那么我们就是要求:
g
k
(
n
)
=
∑
i
=
1
n
[
i
i
s
a
p
r
i
m
e
]
i
k
g_k(n)=\sum_{i=1}^n[\mathrm{i\ is\ a\ prime}]i^k
gk(n)=i=1∑n[i is a prime]ik
这个怎么算,首先将
g
(
n
)
g(n)
g(n)初始化为
∑
i
=
1
n
i
k
\sum_{i=1}^n i^k
∑i=1nik,然后减去不合法的部分(即
i
i
i不是质数的情况)。
即我们从小到大枚举质数
p
p
p,并且定义:
g
k
(
n
)
=
∑
i
=
1
n
[
i
是
质
数
或
者
i
的
最
小
质
因
子
大
于
p
]
i
k
g_k(n)=\sum_{i=1}^n [i是质数或者i的最小质因子大于p]i^k
gk(n)=i=1∑n[i是质数或者i的最小质因子大于p]ik
那么随着
p
p
p的增大,
g
k
(
n
)
g_k(n)
gk(n)会越来越小。每次会减小哪些呢?显然是那些以
p
p
p做最小质因子的情况,也就是
x
=
t
p
x=tp
x=tp,其中
t
t
t的最小质因子不小于
p
p
p。这些
x
x
x的和就是
p
k
g
k
(
n
/
p
)
p^k g_k(n/p)
pkgk(n/p),但是这其中
x
x
x还有可能是小于
p
p
p的质数,需要减去,因此答案准确的说应当是
p
k
(
g
k
(
n
/
p
)
−
g
k
(
p
−
1
)
)
p^k(g_k(n/p)-g_k(p-1))
pk(gk(n/p)−gk(p−1))。这就是要从
g
k
(
n
)
g_k(n)
gk(n)减去这个东西。
下文我们使用的时候,设 s = ⌊ N ⌋ , x ≤ s s=\lfloor\sqrt N\rfloor,x\le s s=⌊N⌋,x≤s,要么我们要问 g k ( x ) g_k(x) gk(x),要么问 g k ( N / x ) g_k(N/x) gk(N/x)。( N N N和 n n n是不一样的, N N N是输入的,全局固定的)。
我们可以注意到,若
a
1
,
a
2
≤
s
a_1,a_2\le s
a1,a2≤s并且
n
/
a
1
=
n
/
a
2
n/a_1=n/a_2
n/a1=n/a2,那么可以说明
a
1
=
a
2
a_1=a_2
a1=a2。
因此我们用
A
(
x
)
=
g
k
(
x
)
,
B
(
x
)
=
g
k
(
N
/
x
)
,
1
≤
x
≤
s
A(x)=g_k(x),B(x)=g_k(N/x),1\le x\le s
A(x)=gk(x),B(x)=gk(N/x),1≤x≤s。(代码里面是s和l)。
显然上文过程
t
t
t是可能包含
p
p
p作为最小质因子的,因此
n
n
n要从大到小枚举,并且对于
n
<
p
2
n<p^2
n<p2的
n
n
n是没有讨论必要的。
转移(注意转移顺序):
B
(
n
)
=
g
k
(
N
/
n
)
,
−
=
p
k
(
g
k
(
n
/
i
p
)
−
g
(
p
−
1
)
)
,
n
≤
min
(
N
/
p
2
,
s
)
∴
w
h
e
n
n
≤
s
/
p
,
B
(
n
)
−
=
p
k
(
B
(
n
p
)
−
A
(
p
−
1
)
)
o
t
h
e
r
w
i
s
e
B
(
n
)
−
=
p
k
(
A
(
n
/
(
i
p
)
)
−
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)=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
~
x
1~x
1~x中,最小质因子不小于
p
r
i
m
e
[
y
]
prime[y]
prime[y]的数字的
F
F
F的和。那么答案显然就是
S
(
n
,
1
)
+
F
(
1
)
S(n,1)+F(1)
S(n,1)+F(1)。
考虑去计算 S ( x , y ) S(x,y) S(x,y),答案分成两部分,
第一部分是 i i i是质数的情况,这个直接用刚刚求出的 g g g函数做一下就可以了;
后半部分考虑枚举 i i i的最小质因子 p r i m e [ z ] > = p r i m e [ y ] prime[z]>=prime[y] prime[z]>=prime[y],那么这个 i = p r i m e [ z ] e × t i=prime[z]^e\times t i=prime[z]e×t,其中 t t t的最小质因子 > p r i m e [ z ] >prime[z] >prime[z](注意是大于)。
这样枚举 z z z和 e ≥ 1 e\ge1 e≥1使得 p r i m e [ z ] e + 1 ≤ x prime[z]^{e+1}\le x prime[z]e+1≤x,然后答案加上 S ( x / ( p r i m e [ z ] e ) , z + 1 ) × F ( p r i m e [ z ] e ) + F ( p r i m e [ 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 = 1 t=1 t=1的情况,由于 F ( p r i m e [ z ] ) F(prime[z]) F(prime[z])已经在之前的 g g g函数中处理过了,所以这里就不用算了)。
注意到这里至少是
p
r
i
m
e
[
z
]
2
≤
x
prime[z]^2\le x
prime[z]2≤x,因此只要枚举到根号
x
x
x的质数即可。
这里一个小细节是为啥没有加上
S
(
x
/
(
p
r
i
m
e
[
z
]
e
+
1
)
,
z
+
1
)
×
F
(
p
r
i
m
e
[
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)(
e
e
e取到最大),这是因为
p
r
i
m
e
[
z
]
prime[z]
prime[z]的
e
+
2
e+2
e+2次是超过了
n
n
n的,那么
p
r
i
m
e
[
z
]
prime[z]
prime[z]的
e
+
1
e+1
e+1次再乘以后面某个大于
p
r
i
m
e
[
z
]
prime[z]
prime[z]的质数也就爆炸了)。
……终于说完了。这东西复杂度为啥是对的我也不知道,反正跑的还挺快就是了。
代码是divcntk的代码,满足
F
(
p
)
=
k
+
1
F(p)=k+1
F(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);
}