Loading

一种质数筛法

考虑 min_25 筛,可以做到 \(O(\frac{n^{0.75}}{\log n})\)

但是 \(n\le 10^{13}\),无法通过。

考虑值域分治,使用树状数组处理 \(1\sim B_1\) 的质数前缀个数。可以把筛质数改成 \(O(n\log \log n)\) 筛,会方便一点,每次可以顺便地扫描每个最小质因子是当前枚举质数的数。

时间 \(O(B_1\log B_1+ \frac{n}{\sqrt B_1 \log n})\),取 \(B_1=\frac{n^{\frac 23}}{\log ^{\frac 43}n}\) 可以做到 \(O(\frac{n^{\frac 23}}{\log ^{\frac 13}n})\)

考虑 \(1\sim B_2\) 直接暴力做,\(B_2+1\sim B_1\) 用树状数组。

\(B_2=\sqrt[6]n,\space B_1=(\frac n{\log n})^{\frac 23}\) 时做到 \(O((\frac n{\log n})^{\frac 23})\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn=4e6+10;
ll n,g[maxn<<1],id1[maxn],id2[maxn],sq,w[maxn<<1],len,sum[maxn],cnt;
int tree[maxn*10];
bool vis[maxn*10];
ll B1,B2;
void add(ll x,ll v){
	while(x<=B1){
		tree[x]+=v; x+=x&-x;
	}
}
ll ask(ll x){
	ll v=0;
	while(x){
		v+=tree[x]; x-=x&-x;
	} return v;
}
ll Id(ll x){
	if(x<=sq) return id1[x];
	return id2[n/x];
}
ll Get(ll x){
	if(x<=B2) return sum[x];
	if(x<=B1) return ask(x)+sum[B2];
	return g[Id(x)];
}
int main(){
	scanf("%lld",&n);
	B1=pow(n/log2(n),0.66), B2=sqrt(n);
	B1=max(B1,B2);
	for(ll i=1;i<=n;i++){
		ll d=n/i, r=n/d;
		if(d<=B1) break; 
		w[++len]=d;
		g[len]=d; i=r;
		if(d<=sq) id1[d]=len;
		else id2[n/d]=len;
		i=r;
	}
	for(ll i=1;i<=B2;i++) sum[i]=i;
	for(ll i=B2+1;i<=B1;i++) add(i,1);
	for(ll i=2;i*i<=n;i++){
		if(vis[i]) continue;
		++cnt;
		ll t=i*i; double inv=1.0/i;
		for(ll j=1;j<=len&&w[j]>=t;j++)
			g[j]-=Get(w[j]*inv+1e-6)-cnt;
		for(ll j=B2;j>=t;j--) sum[j]-=sum[(int)(j*inv+1e-6)]-cnt;
		for(ll j=i*i,o=max(B1,sq);j<=o;j+=i)
			if(!vis[j]){
				vis[j]=true;
				if(j>B2&&j<=B1) add(j,-1);
			}
	}
	printf("%lld",Get(n)-1);
	return 0;
}
posted @ 2024-03-05 21:52  Lgx_Q  阅读(6)  评论(0编辑  收藏  举报