P7588 双重素数(2021 CoE-II A)题解

· · 题解

题目传送门

前置知识:欧拉筛

思路:

$2.$ 从小到大枚举每个筛好的素数,求出各位数字和,由于塔小于等于 $9 \times 8 = 72$,所以可以直接用枚举判断。如果是双重素数,那么放到答案数组储存。 $3.$ 对于每次输入,我们需要找到答案数组中第一个严格大于 $r$ 的数的位置与第一个大于等于 $r$ 的数的位置。因为答案数组是递增的(因为 $2.$ 是从小到大枚举的),于是可以用二分快速找到位置。 有两个很好用的函数:`lower_bound` 和 `upper_bound`,`lower` 是找到一个递增数组里第一个大于等于某数的位置,`upper` 是找到一个递增数组里第一个大于某数的位置(你没看错,只差一个等于)。 它们的用法(找到位置):`lower_bound/upper_bound(a+1,a+n+1,x)-a`。 这里 $a$ 是要寻找的递增数组,$n$ 是这个数组的长度,$x$ 是要找到的数(上文中的“某数”)。这样写是数组存数 $1 \sim n$ 的,如果数组存数是 $0 \sim n-1$,那么把两个 `+1` 删掉即可。 那么输出就是 `upper_bound(ans+1,ans+k+1,r)-ans-(lower_bound(ans+1,ans+k+1,l)-ans)`。 这是我的代码: ```cpp #include<bits/stdc++.h> using namespace std; inline bool sushu(int a) { // 暴力判断数字和是否为素数 if(a==43 || a==47 || a==53 || a==59 || a==61 || a==67 || a==71) { return 1; } if(a==41 || a==37 || a==31 || a==29 || a==23 || a==19 || a==17 || a==13 || a==11 || a==7 || a==5 || a==3 || a==2) { return 1; } return 0; } int dp[10000010]; // double prime bitset<100000001> b; int ans[10000010]; int k=0; void work() { // 欧拉筛 + 筛出双重素数 const int n=100000000; b[1]=1; for(int i=2; i<=n; ++i) { if(!b[i]) { dp[++k]=i; } for(int j=1; j<=k && i*dp[j]<=n; ++j) { b[dp[j]*i]=1; if(!(i%dp[j])) { break; } } } int k1=k; k=0; for(int i=1; i<=k1; ++i) { int sum=0,t=dp[i]; while(t) { sum+=t%10; t/=10; } if(sushu(sum)) { ans[++k]=dp[i]; } } } int main() { work(); int t,l,r; cin>>t; for(; t; --t) { cin>>l>>r; cout<<upper_bound(ans+1,ans+k+1,r)-ans-(lower_bound(ans+1,ans+k+1,l)-ans)<<endl; } return 0; } ``` 另外,我发明了一种方式存储 `bool` 数组值,时间和空间都和 `bitset` 差不多,详见我的[这篇 blog](https://www.luogu.com.cn/blog/wangxiwen/you-hua-bool-shu-zu-kong-jian)。下面是我的用这种方式做的代码: ```cpp #include<bits/stdc++.h> using namespace std; const int Size=100000000; unsigned int B[(Size+32+1)>>5]; inline void change(int wz,int turn) { int add=wz&31; wz>>=5; if(!turn && B[wz]>>add&1 && (B[wz]-=(1<<add))); if(turn && !(B[wz]>>add&1) && (B[wz]+=(1<<add))); } inline bool find(int wz) { return B[wz>>5]>>(wz&31)&1; } inline bool sushu(int a) { if(a==43 || a==47 || a==53 || a==59 || a==61 || a==67 || a==71) { return 1; } if(a==41 || a==37 || a==31 || a==29 || a==23 || a==19 || a==17 || a==13 || a==11 || a==7 || a==5 || a==3 || a==2) { return 1; } return 0; } int dp[10000010]; // double prime int ans[10000010]; int k=0; inline void work() { const int n=100000000; change(1,1); for(register int i=2; i<=n; ++i) { if(!find(i)) { dp[++k]=i; } for(register int j=1; j<=k && i*dp[j]<=n; ++j) { change(dp[j]*i,1); if(!(i%dp[j])) { break; } } } int k1=k; k=0; for(register int i=1; i<=k1; ++i) { int sum=0,t=dp[i]; while(t) { sum+=t%10; t/=10; } if(sushu(sum)) { ans[++k]=dp[i]; } } } int main() { work(); register int t,l,r; cin>>t; for(; t; --t) { cin>>l>>r; cout<<upper_bound(ans+1,ans+k+1,r)-lower_bound(ans+1,ans+k+1,l)<<endl; } return 0; } ```