Description
我们把所有大于整数p的质数称作大质数。
现在我们要统计区间[a,b]中有多少数其至少有一个约数是大质数。
a
,
b
≤
1
0
9
,
  
b
−
a
≤
1
0
8
,
  
p
≤
1
0
6
a,b\le 10^9,\; b-a\le10^8,\; p\le 10^6
a,b≤109,b−a≤108,p≤106
Solution
这道题比较厉害,并不清楚要怎么分类
首先可以想到统计不合法的数的数量,设f(l,r,k)表示l到r之间所有质因数均不超过第k个质数的数量,那么有
f
(
l
,
r
,
k
)
=
{
0
r
<
l
r
−
l
+
1
r
≤
p
k
[
l
=
1
]
k
=
0
f
(
⌈
l
p
k
⌉
,
⌊
r
p
k
⌋
,
k
)
+
f
(
l
,
r
,
k
−
1
)
其
余
情
况
f(l,r,k)=\begin{cases} 0 & r<l \\ r-l+1 & r\le p_k \\ [l=1] & k=0 \\ f(\lceil\frac{l}{p_k}\rceil,\lfloor\frac{r}{p_k}\rfloor,k)+f(l,r,k-1) & 其余情况 \end{cases}
f(l,r,k)=⎩⎪⎪⎪⎨⎪⎪⎪⎧0r−l+1[l=1]f(⌈pkl⌉,⌊pkr⌋,k)+f(l,r,k−1)r<lr≤pkk=0其余情况
首先前面三个都比较显然,最后一个表示的是我们分别算最大质因子恰好为
p
k
p_k
pk和小于
p
k
p_k
pk两部分的答案,这样子递归就可以做了。
然后喜闻乐见的是这道题根据题解的说法可以通过某些预处理来跑过1s的实现,也就是我们需要卡常
Code
#pragma GCC optimize(3)
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
typedef long long LL;
const int N=1000005;
bool np[N+5];
int p[N+5],vec[N+5],cnt;
inline void pre(int n) {
rep(i,2,n) {
if (!np[i]) p[++p[0]]=i;
for (int j=1;i*p[j]<=n&&j<=p[0];++j) {
np[i*p[j]]=1;
if (i%p[j]==0) break;
}
}
}
inline bool check(int v,int k) {
if (v<=N) return vec[v]<=p[k];
register int x=v;
for (register int i=1,lim=sqrt(v)+1;p[i]<=lim;++i) {
register int wjp=p[i];
if (x%wjp==0) {
if (i>k) return 0;
while (x%wjp==0) x/=wjp;
}
}
return (x<=p[k]);
}
inline int solve(int l,int r,int k) {
if (l>r) return 0;
if (k==0) return (l==1);
if (r<=p[k]) return r-l+1;
if (l==r) return check(l,k);
return solve(l,r,k-1)+solve((l-1)/p[k]+1,r/p[k],k);
}
int main(void) {
freopen("data.in","r",stdin);
pre(N); int l,r,k,ans=0;
rep(i,1,100000) {
int x=i;
for (int j=1;p[j]*p[j]<=i;++j) {
if (x%p[j]) continue;
while (x%p[j]==0) x/=p[j];
vec[i]=std:: max(vec[i],p[j]);
}
vec[i]=std:: max(vec[i],x);
}
scanf("%d%d%d",&k,&l,&r);
rep(i,1,p[0]) if (p[i]>k) {
printf("%d\n", r-l+1-solve(l,r,i-1));
return 0;
}
return 0;
}