积性函数求和问题的一种筛法
command_block · · 个人记录
stO zzt Orz
本文给出了一种时间,空间复杂度均为
前置知识 : 杜教筛(+贝尔级数+powerful number)
符号约定 :
-
-
-
涉及的积性函数均有
F(1)=1 。 -
对函数
F ,给出范围N ,对所有m=\lfloor N/i\rfloor 求解S_F(m) ,这种操作称之为块筛,有时省略范围。大
N 一般指题目中给出的N ,而小n 则用作分析。
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
1.质数的 c 次幂前缀和(优化)
前半部分见 Min_25筛小记。
- ③
当然,可以考虑利用朴素方法处理一部分
每次新加质数的时候,会将
递推时次数为
总的复杂度就是
- ④
考虑省去递推运算中的部分树状数组查询操作。
注意到
此时,
也就是说,在第
对于每个
这部分总复杂度是
这样子的总复杂度是
- ⑤
考虑在
此时还需筛去的数满足最小素因子大于
笔者在
参考 : 当
取
令
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
2.积性函数求和
-
问题概述 :
给出一个积性函数
F(n) ,满足如下性质:给出范围
N ,求出F 的块筛。 -
根据
不妨令
我们以
设
(若素因子次数为
仿照前面
设
按照定义有
可以发现
则
可以据此写出
边界 :
- 解释 :
当
当
当
现在看来,我们似乎需要枚举所有的质数。
注意到当
当
直接使用上述算法即可。
这东西常数较小 (远小于洲阁筛,同样精细实现的话比Min_25不相上下)。
在 DIVCNTK 拿到了 LG·Rk1 ,原站 SPOJ·Rk5。
把
目前在 DIVCNT3 甚至比我原来精细实现的杜教筛还快。
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define ull unsigned long long
#define lbit(x) ((x)&-(x))
#define Limit 805000
using namespace std;
int tn,lim,lp,p[Limit>>3],c[Limit],e[Limit];
void getsth()
{
for (int i=2,t;i<=lim;i++){
if (!e[i])c[p[++tn]=i]=1;
for (int j=1;j<=tn&&(t=p[j]*i)<=lim;j++){
e[t]=p[j];
if (i%p[j]==0)break;
if(c[i])c[t]=c[i]+1;
}
}p[tn+1]=Limit;
}
ull N,K,tH[35];
void getPN()
{
tH[0]=1;
for (int k=2;k<=33;k++)
tH[k]=(K-1)*k+1-K*tH[k-1];
}
int tot;
ull t[Limit],x[Limit],tag;
inline void upd(int p,ull x){
while(p<=lim){t[p]+=x;p+=lbit(p);}
}
inline ull qry(int p){
ull sum=0;
while(p){sum+=t[p];p^=lbit(p);}
return sum;
}
void getP()
{
for (tot=1;N>(ull)lim*tot;tot++)x[tot]=N/tot-1;
tot--;
for (int i=1;i<=lim;i++)t[i]=lbit(i);
upd(1,-1);
for (int tp=1;tp<=lp;tp++){
int P=p[tp],u=min(N/((ull)P*P),(ull)tot),
uu=min(u,tot/P);
ull sav=qry(P-1),t=N/P;
for (int i=1;i<=uu;i++)
x[i]-=x[P*i]-sav;
if (t<(1ull<<31))
for (int tt=t,i=uu+1;i<=u;i++)
x[i]-=qry(tt/i)-sav;
else
for (int i=uu+1;i<=u;i++)
x[i]-=qry(t/i)-sav;
if ((ull)P*P<=lim)
for (int j=P*P;j<=lim;j+=P)
if (e[j]==P)upd(j,-1);
}
}
void sieve()
{
for (lp=1;(ull)p[lp]*p[lp]<=N;lp++);
getP();
memset(t,0,sizeof(ull)*(lim+5));
for (int i=1;i<=tot;i++)x[i]=K*(x[i]-lp);
for (int i=lp+1;p[i]<=lim;i++)upd(p[i],K);
ull tK[35],tag=0;tK[0]=1;
for (int k=1;k<=33;k++)tK[k]=tK[k-1]*K;
for (int i=lp;i;i--){
int P=p[i],u=min(N/((ull)P*P),(ull)tot),
uu=min(u,tot/P);
for (int i=1;i<=uu;i++)
x[i]+=K*(x[P*i]+tag);
ull t=N/P;
if (t<(1ull<<31))
for (int tt=t,i=uu+1;i<=u;i++)
x[i]+=K*qry(tt/i);
else
for (int i=uu+1;i<=u;i++)
x[i]+=K*qry(t/i);
upd(P,K);tag+=K;
if ((ull)P*P<=lim)
for (int j=P*P;j<=lim;j+=P)
if (e[j]==P&&c[j])upd(j,tK[c[j]]);
}for (int i=1;i<=tot;i++)x[i]+=1+tag;
upd(1,1);lim=sqrtl(N);
for (int i=tot+1;i<=lim;i++)x[i]=qry(N/i);
tot=lim;
}
inline ull S(ull d){
if (d<=tot)return x[d];
return qry(N/d);
}
ull ans;
void dfs(ull n,ull num,int t)
{
ans+=num*S(n);
for (int i=t,k;i<=lp;i++){
if ((ull)p[i]*p[i]>N/n)return ;
k=2;
for (ull x=(ull)p[i]*p[i];x*n<=N;k++,x*=p[i])
dfs(n*x,num*tH[k],i+1);
}
}
#define gL(N) max((int)max(1.75*pow(N/log2(N),0.6666),sqrt(N)+5),100)
void solve()
{
lim=gL(N);if (lim>N)lim=N;
getPN();sieve();
ans=0;dfs(1,1,1);
printf("%llu\n",ans);
}
ull Sn[10050],Sk[10050],mN;
int T;
int main()
{
scanf("%d",&T);
for (int i=1;i<=T;i++){
scanf("%llu%llu",&Sn[i],&Sk[i]);
Sk[i]++;mN=max(mN,Sn[i]);
}lim=gL(mN);if (lim>mN)lim=mN;
getsth();
for (int i=1;i<=T;i++){
N=Sn[i];K=Sk[i];
solve();
}return 0;
}