你最愿意做的哪件事,才是你的天赋所在

0%

LibreOJ#6235-区间素数个数

题目链接

LibreOj#6235区间素数个数

思路

用min_25筛的前半部分解决,有点类似DP融合埃筛的解法

根据这个式子,举一个例子让我们更好理解这个式子
g(10,0)=9;
因为还没有筛选过依次,所以等于9
g(10,1)=g(10,0)-g(5,0)-g(1,0)=9-4-0=5;
筛选过依次之后把以2为最小质因子的数(4,6,8,10)给筛掉了剩下5.这里要注意,我们要用到g(5,0)与g(1,0)所以我们每次处理的时候不能只处理g(10)的。
g(10,2)=g(10,1)-g(3,1)-g(2,0)=5-0-1=4;
这里用到了g(3,1)与g(2,0)需要在上面处理好
g(10,3)=g(10,2)-g(5,2)-g(3,1)=4-0-0=4;
最后一步就不需要了,因为后面两个式子一定等于0,这是因为$prime_3^2=5^2>10$了,所以25之内的合数都已经被排除掉了,就不需要再考虑了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 4e5+10;
ll n,g[N<<1],a[N<<1];
int id,cnt,sn,prime[N];
ll Id(ll x){return x<=sn?x:id-n/x+1;}
int main()
{
scanf("%lld",&n);
sn=sqrt(n);
for(ll i=1;i<=n;i=a[id]+1)a[++id]=n/(n/i),g[id]=a[id]-1;
for(ll i=2;i<=sn;i++)if(g[i]!=g[i-1])
{
prime[++cnt]=i;
ll sum =1ll*i*i;
for(int j=id;a[j]>=sum;j--)g[j]-=g[Id(a[j]/i)]-g[i-1];
//这里表示[n/i]-[i之内的质数个数以及最小质因子大于i的个数之和]
}
printf("%lld\n",g[id]);
}

题目链接

SPOJ-APS2

思路

刚学min25的时候写的一道题,很巧妙的思维题,用min25来维护质数的前缀和,以及个数,然后再埃筛的过程化的时候把合数的答案给加上去。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<cstdio>
#include<math.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int N = 1111115;
ll n,g[N<<1],a[N<<1],h[N<<1],t;
//g表示区间素数和,h用来计算被筛去的个数
int id,cnt,sn,prime[N];
ll Id(ll x){return x<=sn?x:id-n/x+1;}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
ull ans=0;
cnt=0;
id=0;
scanf("%lld",&n);
sn=sqrt(n);
for(ll i=1;i<=n;i=a[id]+1)a[++id]=n/(n/i),g[id]=(a[id]%2==0?(ull)a[id]/2*(a[id]+1):(ull)(a[id]+1)/2*a[id])-1,h[id]=a[id]-1;
for(ll i=2;i<=sn;i++)if(h[i]!=h[i-1])
{
prime[++cnt]=i;
ll sq=1ll*i*i;
for(ull j=id;a[j]>=sq;j--)
{
g[j]-=(g[t=Id(a[j]/i)]-g[i-1])*i;//相当于[n/i]的前缀和-[i之内的素数个数以及不以i为最小素因子的个数总和]的前缀和
h[j]-=(h[t]-h[i-1]);//相当于[n/i]-[i之内的素数个数以及不以i为最小素因子的个数总和]
if(j==id)ans=(ans+i*(h[t]-h[i-1]));//计算值即可
}
}
printf("%llu\n",ans+g[id]);
}
}
-------------你最愿意做的哪件事才是你的天赋所在-------------