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)。

posted @ 2020-07-06 06:21  With_penguin  阅读(149)  评论(0编辑  收藏  举报