专栏文章
P4213
P4213题解参与者 7已保存评论 8
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 8 条
- 当前快照
- 1 份
- 快照标识符
- @minxdnkw
- 此快照首次捕获于
- 2025/12/02 09:54 3 个月前
- 此快照最后确认于
- 2025/12/02 09:54 3 个月前
杜教筛(给定积性函数 ,已知 块筛,求 的块筛)可以不借助 zak 筛手法做到 。
若 是完全积性函数,我们可以用 Min_25 筛的第一部分筛除 以内的质因数得到 ,这是 的。我们也可以用 Min_25 筛的第二部分从 恢复到 (注意这两步我们会在 间切换,Min_25 筛时要保留 以内质数)。取 ,此部分 。
对 做杜教筛,考虑使用狄利克雷双曲线法。对 ,有 ,移项得到递归式 。因为 的密度是 ,求块筛单点是 的。
线性筛 个 ,块筛卷积部分是 的。实际上我们有手段避免线性筛到 。令 ,考虑搜索 以内的 -rough 数的质因数分解。这样需要筛出 的质数。我们发现要搜到的合数含有的质因数不超过 ,考虑构造一个完全积性函数 ,将 先设为 筛除 内质数的结果,然后用上面的 DFS 修改贡献。这样就不需要搜质数了。
剩下的问题是 可能不是完全积性的。但是根据 PN 筛道理,我们可以在 的时间内在 的两个积性函数间转换,因此将 切换为 ,用上述做法,不是瓶颈。复杂度 。
CPP#include<bits/stdc++.h>
using namespace std;
int t,bn,bn1,bn2,num,prime[46345],cnt,pi[1664515],mu[1664515];
long long n,r[92686],g[92686],f[92686];
bool vis[46345];
int mu_init(int n,int prime[],int mu[],bool vis[],int cnt=0){
mu[1]=1;
for(int i=2;i<=n;i++)vis[i]=1;
for(int i=2;i<=n;i++){
if(vis[i])prime[++cnt]=i,mu[i]=-1;
for(int j=1,v,p;i*prime[j]<=n;j++){
p=prime[j],v=i*p,vis[v]=0;
if(i%p==0){
mu[v]=0;
break;
}
else mu[v]=-mu[i];
}
}
return cnt;
}
int id(long long x){
return x<=bn?x:num-n/x+1;
}
void dfs(int dep,int now,int v){
if(now>bn||!vis[now])f[id(now)]+=v+1;
while(dep<=cnt&&prime[dep]<=min(bn1/now,bn)){
int p=prime[dep],nxt=now;
for(int i=1;1ll*nxt*p<=bn1;i++,nxt*=p)dfs(dep+1,nxt*p,i==1?-v:0);
dep++;
}
}
int main(){
cnt=mu_init(46340,prime,mu,vis);
for(int i=1;i<=46340;i++)pi[i]=pi[i-1]+vis[i];
cin>>t;
while(t--){
cin>>n,bn=sqrt(n),bn1=pow(n,2.0/3),bn2=pow(n,1.0/6);
for(long long l=1;l<=n;l=r[num]+1)r[++num]=n/(n/l),g[num]=r[num]-1;
for(int j=1;j<=cnt&&prime[j]<=bn2;j++){
int p=prime[j];
long long t=p*p;
for(int i=num;r[i]>=t;i--)g[i]-=g[id(r[i]/p)]-g[p-1];
}
for(int i=1;i<=num;i++)g[i]-=pi[min(r[i],(long long)bn2)]-1;
for(int i=1;i<=num;i++)f[i]=0;
dfs(pi[bn2]+1,1,1);
for(int i=1;i<=num&&r[i]<=bn1;i++)f[i]+=f[i-1];
for(int i=1;i<=num&&r[i]<=bn1;i++)f[i]-=g[i];
vector<int>rough;
for(int i=1;i<=bn;i++)if(g[i]!=g[i-1])rough.push_back(i);
for(int i=1;i<=num;i++){
if(r[i]<=bn1)continue;
int bn=sqrt(r[i]);
f[i]=f[bn]*g[bn]+1;
for(int j=0;j<rough.size()&&rough[j]<=bn;j++)f[i]-=mu[rough[j]]*g[id(r[i]/rough[j])];
for(int j=1;j<rough.size()&&rough[j]<=bn;j++)f[i]-=f[id(r[i]/rough[j])];
}
for(int i=1;i<=num;i++)f[i]-=pi[min(r[i],(long long)bn2)]+1;
for(int j=pi[bn2];j>=1;j--){
int p=prime[j];
long long t=1ll*p*p;
for(int i=num;r[i]>=t;i--)f[i]-=f[id(r[i]/p)]+j;
}
for(int i=1;i<=num;i++)f[i]++;
long long phi=0;
for(int i=1;i<=num;i++)phi+=(f[i]-f[i-1])*(n/r[i])*(n/r[i]+1)/2;
cout<<phi<<' '<<f[num]<<'\n',num=0;
}
return 0;
}
关于 PN 筛求块筛:
定义 Powerful Number(PN)为所有质因数的次数都大于一的数。可以证明 PN 的个数是 级别的:显然 PN 都可以表示为形如 的数,个数为 。
假如要求 的前缀和,构造一个容易求和的积性函数 ,满足对于任意质数 ,满足 。
存在一个函数 使得 ,那么 。因此 。 为积性函数,所以 只在 PN 处有值,因为其他函数都可以分离出一项次数为 。
PN 可以通过搜索求出。现在只需要求 的值。可以求所有 的值,再乘出 PN 的值。若函数特殊,可以手玩。一般可以根据 有 ,则 ,暴力求逆即可,只需要对根号以下质数这么做,复杂度不超过 。
已知 的块筛,我们得以 求 的前缀和的单点。求块筛则可以做到 。
我们要对每个整除位置求 。对块筛 差分。若 ,枚举 贡献到对应位置,。
剩下的整除位置可表示为 。若 ,枚举 ,能贡献的 的范围是 ,可用块筛表示。,复杂度 。 同理。
若 ,枚举 是 PN。若 ,枚举 加到上面。否则 。枚举 , 的范围是 。此部分复杂度 。
CPPvoid powerful_number(long long n,int sg[],int sf[]){
vector<pair<long long,int> >pn;
for(int i=1;i<=cnt;i++){
int p=prime[i],gp[40];
long long pk=p;
hp[i][0]=1;
for(int j=1;pk<=n;j++,pk*=p){
gp[j]=calcg(pk,p),hp[i][j]=calcf(p,j);
for(int k=1;k<=j;k++)hp[i][j]=(hp[i][j]-1ll*gp[k]*hp[i][j-k]%mod+mod)%mod;
}
}
dfs(1,1,1,n,pn),sort(pn.begin(),pn.end());
for(int i=1,j=0,now=0;i<=num;i++){
while(j<pn.size()&&pn[j].first<=r[i])now=(now+pn[j++].second)%mod;
sh[i]=now;
}
for(int i=1;i<=bn;i++){
for(int j=1;i*j<=bn;j++){
int l=id(max(n/i/(j+1),(long long)bn)),r=id(n/i/j);
if(sh[i]!=sh[i-1]){
sf[i*j]=(sf[i*j]+1ll*(sh[i]-sh[i-1]+mod)*(sg[j]-sg[j-1]+mod))%mod;
sf[num-j+1]=(sf[num-j+1]+1ll*(sh[i]-sh[i-1]+mod)*(sg[r]-sg[l]+mod))%mod;
}
sf[num-j+1]=(sf[num-j+1]+1ll*(sg[i]-sg[i-1]+mod)*(sh[r]-sh[l]+mod))%mod;
}
}
for(int i=0;i<pn.size()&&pn[i].first<=bn;i++){
long long x=pn[i].first,lim=sqrt(n/x);
for(int j=bn/x+1;j<=lim;j++)sf[id(x*j)]=(sf[id(x*j)]+1ll*pn[i].second*(sg[j]-sg[j-1]+mod))%mod;
for(int j=max((n+x*bn-1)/(x*bn)-1,1ll);j<=lim;j++){
sf[num-j+1]=(sf[num-j+1]+1ll*pn[i].second*(sg[min(n/x/j,(long long)bn)]-sg[id(max(n/x/(j+1),(long long)lim))]+mod))%mod;
}
}
for(int i=1;i<=num;i++)sf[i]=(sf[i]+sf[i-1])%mod;
}
相关推荐
评论
共 8 条评论,欢迎与作者交流。
正在加载评论...