感觉和 m i n _ 25 min\_25 min_25筛挺像的???(大雾
听说貌似可以求很多东西的样子,于是就去入了个门(然而啥题都不会做。
参考博客:litble巨佬的与扩展埃氏筛(min_25筛?)玩耍
e . g . 1 e.g.1 e.g.1 求 1 1 1~ n \ n n之间的素数个数, n ≤ 1 0 11 n\le10^{11} n≤1011
传送门
貌似线性筛会 T L E TLE TLE啊(显而易见
然后就有了扩展埃氏筛这个东西。
我们考虑埃氏筛的过程:
现在最开始有一排数 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n,我们只知道 1 1 1不是质数,而并不知道 2 2 2~ n n n哪些不是质数,于是每次发现一个质数之后就会把它的倍数全部标记成为合数,之后访问到的没有打过标记的即为质数。
换到这道题上面:
我们令 f ( x ) f(x) f(x)表示 1 1 1~ x \ x x之间的质数个数,由于最开始不知道哪些是合数,因此初始化时 f ( x ) = x − 1 f(x)=x-1 f(x)=x−1
之后直接往后枚举,假设现在正在处理质数 p p p,那么对于 f ( i ) f(i) f(i)显然需要扣掉以 p p p为最小质因子的合数,也就是 f ( ⌊ i p ⌋ ) − f ( p − 1 ) f(\left\lfloor\frac{i}{p}\right\rfloor)-f(p-1) f(⌊pi⌋)−f(p−1),(因为此时 f ( ⌊ i p ⌋ ) f(\left\lfloor\frac{i}{p}\right\rfloor) f(⌊pi⌋)已经被处理过并且将小于 p p p的质数也算入内了因此要扣掉)
即: f ( i ) − = f ( ⌊ i p ⌋ ) − f ( p − 1 ) f(i)-=f(\left\lfloor\frac{i}{p}\right\rfloor)-f(p-1) f(i)−=f(⌊pi⌋)−f(p−1)
那么我们用两个数组 f 1 , f 2 f1,f2 f1,f2来记录 f f f的部分,其中 f 1 ( x ) = f ( x ) , f 2 ( x ) = f ( n / x ) f1(x)=f(x),f2(x)=f(n/x) f1(x)=f(x),f2(x)=f(n/x)
于是就可以处理了。
#include
#define ri register int
using namespace std;
const int N=1e6+5;
typedef long long ll;
ll f1[N],f2[N],n;
int lim;
int main(){
cin>>n;
lim=sqrt(n);
for(ri i=1;i<=lim;++i)f1[i]=i-1,f2[i]=n/i-1;
for(ri p=2;p<=lim;++p){
if(f1[p]==f1[p-1])continue;
for(ri i=1;i<=lim/p;++i)f2[i]-=f2[i*p]-f1[p-1];
for(ri i=lim/p+1;(ll)i<=n/(ll)p/(ll)p&&i<=lim;++i)f2[i]-=f1[n/i/p]-f1[p-1];
for(ri i=lim;i>=(ll)p*p;--i)f1[i]-=f1[i/p]-f1[p-1];
}
cout<<f2[1];
return 0;
}
e . g . 2 e.g.2 e.g.2 计算给定区间内所有质数之和, L , R ≤ 1 0 10 L,R\le10^{10} L,R≤1010
传送门
解法几乎同上。
f f f函数的递推式是 f ( i ) − = ( f ( ⌊ i p ⌋ ) − f ( p − 1 ) ) ∗ p f(i)-=(f(\left\lfloor\frac{i}{p}\right\rfloor)-f(p-1))*p f(i)−=(f(⌊pi⌋)−f(p−1))∗p
建议初学者自己手推一下。
代码:
#include
#define ri register int
using namespace std;
const int N=1e6+5;
typedef long long ll;
const double eps=1e-12;
double f1[N],f2[N];
ll L,R;
inline double S(const ll&x){return (double)(x+1)*(double)x/2.0;}
inline double query(ll n){
if(!n)return 0;
ll lim=sqrt(n);
for(ll i=1;i<=lim;++i)f1[i]=S(i),f2[i]=S(n/i);
for(ll p=2;p<=lim;++p){
if(fabs(f1[p]-f1[p-1])<eps)continue;
double t=f1[p-1];
for(ri i=1;i<=lim/p;++i)f2[i]-=(f2[i*p]-t)*(double)p;
for(ll i=lim/p+1;i<=lim&&i<=n/p/p;++i)f2[i]-=(double)(f1[n/i/p]-t)*(double)p;
for(ll i=lim;i>=p*p;--i)f1[i]-=(f1[i/p]-t)*(double)p;
}
return f2[1];
}
int main(){
cin>>L>>R;
printf("%.0lf",query(R)-query(L-1));
return 0;
}
e . g . 3 e.g.3 e.g.3
多组数据,求 S k ( n ) = ∑ i = 1 n σ 0 ( i k ) . S_k(n) = \sum _{i=1}^n \sigma_0(i^k). Sk(n)=i=1∑nσ0(ik).
满足 k , n ≤ 1 0 10 , σ 0 ( x ) k,n\le10^{10},\sigma_0(x) k,n≤1010,σ0(x)表示 x x x的约数个数。
传送门
质数的处理挺简单的。
现在还需要处理合数。
考虑唯一分解定理,发现可以不断枚举当前可能的最小质因子的幂来统计答案。
可以看代码中的解析:
#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
typedef unsigned long long ll;
inline ll read(){
ll ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int N=2e6+5;
ll n,k,lim,tot,ans,f1[N],f2[N];
int pri[N];
inline void init(ll x){
lim=sqrt(x),tot=0;
if(x<=1)return;
for(ri i=1;i<=lim;++i)f1[i]=i-1,f2[i]=x/i-1;
ll t;
for(ri p=1;p<=lim;++p){
if(f1[p]==f1[p-1])continue;
pri[++tot]=p,t=f1[p-1];
for(ri i=1;i<=lim/p;++i)f2[i]-=f2[i*p]-t;
for(ri i=lim/p+1,up=min((ll)lim,n/(ll)p/(ll)p);i<=up;++i)f2[i]-=f1[x/i/p]-t;
if(lim>=(ll)p*p)for(ri i=lim,up=p*p;i>=up;--i)f1[i]-=f1[i/p]-t;
}
}
inline void solve(int pos,ll pre,ll up){
ll t=up<=lim?f1[up]:f2[n/up];
//统计一坨f(x)的贡献,其中f(x)的最大质因子为1且之前质因子的贡献为pre
ans+=(k+1)*pre*(t-f1[pri[pos-1]]);
for(ri i=pos;i<=tot;++i){
if(up<(ll)pri[i]*pri[i])return;
for(ll mult=pri[i],tim=1;mult<=up;mult*=pri[i],++tim){//枚举质数幂次统计答案
if(mult*pri[i]<up)solve(i+1,pre*(k*tim+1),up/mult);//看之后能不能更大质因子
if(tim>=2)ans+=pre*(k*tim+1);//计算f(x)的最大质因子不为1的贡献
}
}
}
int main(){
for(ri tt=read();tt;--tt){
n=read(),k=read(),ans=1;
init(n),solve(1,1,n);
cout<<ans<<'\n';
}
return 0;
}
e . g . 4 e.g.4 e.g.4 求 f ( i ) f(i) f(i)的前缀和, f ( i ) f(i) f(i)满足:
f ( 1 ) = 1 f(1)=1 f(1)=1
f ( p c ) = p x o r c , p ∈ p r i m e f(p^c)=p\ xor\ c,p\in prime f(pc)=p xor c,p∈prime
f ( a b ) = f ( a ) f ( b ) , g c d ( a , b ) = 1 f(ab)=f(a)f(b),gcd(a,b)=1 f(ab)=f(a)f(b),gcd(a,b)=1
跟上一道题差不多,预处理时要计算出质数异或1的前缀和。
可以自己手推一下。
代码:
#include
#define ri register int
using namespace std;
const int mod=1e9+7,N=2e6+5,inv2=5e8+4;
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
ll n,lim;
int f1[N],f2[N],s1[N],s2[N],pri[N],tot=0,ans;
inline int S(const int&x){return mul(mul(x,x+1),inv2);}
inline void init(ll x){
lim=sqrt(x),tot=0;
if(x<=1)return;
for(ri i=1;i<=lim;++i)f1[i]=i-1,f2[i]=dec(x/i%mod,1),s1[i]=dec(S(i),1),s2[i]=dec(S(x/i%mod),1);
for(ri tf,ts,p=1;p<=lim;++p){
if(f1[p]==f1[p-1])continue;
pri[++tot]=p,tf=f1[p-1],ts=s1[p-1];
for(ri i=1;i<=lim/p;++i){
Dec(f2[i],dec(f2[i*p],tf));
Dec(s2[i],mul(dec(s2[i*p],ts),p));
}
for(ri i=lim/p+1,up=min(lim,x/(ll)p/(ll)p);i<=up;++i){
Dec(f2[i],dec(f1[x/i/p],tf));
Dec(s2[i],mul(dec(s1[x/i/p],ts),p));
}
if(lim>=(ll)p*p)for(ri i=lim,up=p*p;i>=up;--i){
Dec(f1[i],dec(f1[i/p],tf));
Dec(s1[i],mul(dec(s1[i/p],ts),p));
}
}
for(ri i=1;i<=lim;++i){
Dec(s1[i],f1[i]);
if(i>=2)Add(s1[i],2);
Dec(s2[i],f2[i]);
if(n/i>=2)Add(s2[i],2);
}
}
inline void solve(int pos,int pre,ll up){
int t=up<=lim?s1[up]:s2[n/up];
Add(ans,mul(pre,dec(t,s1[pri[pos-1]])));
for(ri i=pos;i<=tot;++i){
if(up<(ll)pri[i]*pri[i])return;
for(ll mult=pri[i],tim=1;mult<=up;mult*=pri[i],++tim){
if(mult*pri[i]<up)solve(i+1,mul(pre,pri[i]^tim),up/mult);
if(tim>=2)Add(ans,mul(pre,pri[i]^tim));
}
}
}
int main(){
cin>>n,ans=1;
init(n),solve(1,1,n);
cout<<ans;
return 0;
}