SP34096 DIVCNTK - Counting Divisors (general)
我竟然为这题的函数不能拆成完成积性函数想了两个小时(捂脸)。
看到这么大的数据范围和求前缀和的要求,我们首先想到了Min_25筛。
设 \(f(i)=\sigma_0(i^k)\),则 \(f(p^e)=ke+1\)。
啊啊啊这个 \(f\) 没有办法拆成完全积性函数,所以我们就求不了 \(f\) 在质数处的前缀和了,所以这个题就没有办法用Min_25筛求了。(我就是一直卡在这里)
然后我们发现了这个 \(f(p)=k+1\),也就是 \(f\) 在质数处的点值其实是常数,所以我们只要求出质数个数在乘以这个常数就能求出 \(f\) 处的前缀和了。我还是太naive了。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
using namespace std;
typedef unsigned long long LL;
const int N=3000000;
LL n,k,cnt,tot,Sqr,g[N+9],w[N+9],p[N];
int ind1[N+9],ind2[N+9],a[N+9];
void prework()
{
for (register int i=2;i<=N;++i)
{
if(!a[i])
a[i]=i,p[++cnt]=i;
for (register int j=1;j<=cnt;++j)
{
if(p[j]>a[i]||p[j]>N/i)
break;
a[p[j]*i]=p[j];
}
}
}
inline void init()
{
scanf("%llu %llu",&n,&k);
Sqr=(LL)sqrt(n);
tot=0;
for (register LL i=1;i<=n;)
{
LL j=n/(n/i),q=n/i;
w[++tot]=q;
g[tot]=q-1;
if(q<=Sqr) ind1[q]=tot;
else ind2[j]=tot;
i=j+1;
}
for (register int i=1;i<=cnt&&p[i]*p[i]<=n;++i)
for (register int j=1;j<=tot&&p[i]*p[i]<=w[j];++j)
{
LL q=w[j]/p[i];
int k=q<=Sqr?ind1[q]:ind2[n/q];
g[j]-=g[k]-(i-1);
}
}
inline LL S(LL x,int y)
{
if(p[y]>=x) return 0;
int K=x<=Sqr?ind1[x]:ind2[n/x];
LL res=(g[K]-y)*(k+1);
for (register int i=y+1;p[i]*p[i]<=x&&i<=cnt;++i)
{
LL pe=p[i];
for (int e=1;pe<=x;++e,pe*=p[i])
res+=(k*e+1)*(S(x/pe,i)+(e!=1));
}
return res;
}
void work()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
printf("%llu\n",S(n,0)+1);
}
// printf("%.2lf\n",(double)clock()/CLOCKS_PER_SEC);
}
int main()
{
prework();
work();
return 0;
}
哦对了别问我为什么能跑这么多组数据(实际上本地我跑十组数据就用了6s)。
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!