[学习笔记] SPOJ DIVCNTK - Min_25筛

这些各种乱七八糟的筛法真难懂……
首先Min_25筛的基本思想就是在不停的枚举最小质因子。
zzt的论文根本看不懂。

下文所有除法为了方便都是下取整。

过程是这样的,我们先求这样一个东西:
g ( n ) = ∑ i = 1 n [ i   i s   a   p r i m e ] F ( i ) g(n)=\sum_{i=1}^n [\mathrm{i\ is\ a\ prime}]F(i) g(n)=i=1n[i is a prime]F(i)
通常题目会保证 F ( p ) F(p) F(p)是一个系数固定的低次多项式,假设是k次,那么我们就是要求:
g k ( n ) = ∑ i = 1 n [ i   i s   a   p r i m e ] i k g_k(n)=\sum_{i=1}^n[\mathrm{i\ is\ a\ prime}]i^k gk(n)=i=1n[i is a prime]ik
这个怎么算,首先将 g ( n ) g(n) g(n)初始化为 ∑ i = 1 n i k \sum_{i=1}^n i^k i=1nik,然后减去不合法的部分(即 i i i不是质数的情况)。
即我们从小到大枚举质数 p p p,并且定义:
g k ( n ) = ∑ i = 1 n [ i 是 质 数 或 者 i 的 最 小 质 因 子 大 于 p ] i k g_k(n)=\sum_{i=1}^n [i是质数或者i的最小质因子大于p]i^k gk(n)=i=1n[iip]ik
那么随着 p p p的增大, g k ( n ) g_k(n) gk(n)会越来越小。每次会减小哪些呢?显然是那些以 p p p做最小质因子的情况,也就是 x = t p x=tp x=tp,其中 t t t的最小质因子不小于 p p p。这些 x x x的和就是 p k g k ( n / p ) p^k g_k(n/p) pkgk(n/p),但是这其中 x x x还有可能是小于 p p p的质数,需要减去,因此答案准确的说应当是 p k ( g k ( n / p ) − g k ( p − 1 ) ) p^k(g_k(n/p)-g_k(p-1)) pk(gk(n/p)gk(p1))。这就是要从 g k ( n ) g_k(n) gk(n)减去这个东西。

下文我们使用的时候,设 s = ⌊ N ⌋ , x ≤ s s=\lfloor\sqrt N\rfloor,x\le s s=N ,xs,要么我们要问 g k ( x ) g_k(x) gk(x),要么问 g k ( N / x ) g_k(N/x) gk(N/x)。( N N N n n n是不一样的, N N N是输入的,全局固定的)。

我们可以注意到,若 a 1 , a 2 ≤ s a_1,a_2\le s a1,a2s并且 n / a 1 = n / a 2 n/a_1=n/a_2 n/a1=n/a2,那么可以说明 a 1 = a 2 a_1=a_2 a1=a2
因此我们用 A ( x ) = g k ( x ) , B ( x ) = g k ( N / x ) , 1 ≤ x ≤ s A(x)=g_k(x),B(x)=g_k(N/x),1\le x\le s A(x)=gk(x),B(x)=gk(N/x),1xs。(代码里面是s和l)。

显然上文过程 t t t是可能包含 p p p作为最小质因子的,因此 n n n要从大到小枚举,并且对于 n &lt; p 2 n&lt;p^2 n<p2 n n n是没有讨论必要的。
转移(注意转移顺序):
B ( n ) = g k ( N / n ) , − = p k ( g k ( n / i p ) − g ( p − 1 ) ) , n ≤ min ⁡ ( N / p 2 , s ) ∴ w h e n   n ≤ s / p , B ( n ) − = p k ( B ( n p ) − A ( p − 1 ) ) o t h e r w i s e   B ( n ) − = p k ( A ( n / ( i p ) ) − A ( p − 1 ) ) A ( n ) = g k ( n ) , − = p k ( g k ( n / p ) − g k ( p − 1 ) ) = p k ( A ( n / p ) − A ( p − 1 ) ) B(n)=g_k(N/n),-=p^k(g_k(n/ip)-g(p-1)),n\le \min (N/p^2,s) \\ \therefore \mathrm{when\ }n\le s/p,B(n)-=p^k(B(np)-A(p-1))\\ \mathrm{otherwise\ }B(n)-=p^k(A(n/(ip))-A(p-1))\\ A(n)=g_k(n),-=p^k(g_k(n/p)-g_k(p-1))=p^k(A(n/p)-A(p-1)) B(n)=gk(N/n),=pk(gk(n/ip)g(p1)),nmin(N/p2,s)when ns/p,B(n)=pk(B(np)A(p1))otherwise B(n)=pk(A(n/(ip))A(p1))A(n)=gk(n),=pk(gk(n/p)gk(p1))=pk(A(n/p)A(p1))
(上面虽然看上去好长其实很好推,之不过我把所有细节写下来了而已)

然后考虑维护答案。
首先记 S ( x , y ) S(x,y) S(x,y)表示 1 ~ x 1~x 1x中,最小质因子不小于 p r i m e [ y ] prime[y] prime[y]的数字的 F F F的和。那么答案显然就是 S ( n , 1 ) + F ( 1 ) S(n,1)+F(1) S(n,1)+F(1)

考虑去计算 S ( x , y ) S(x,y) S(x,y),答案分成两部分,

第一部分是 i i i是质数的情况,这个直接用刚刚求出的 g g g函数做一下就可以了;

后半部分考虑枚举 i i i的最小质因子 p r i m e [ z ] &gt; = p r i m e [ y ] prime[z]&gt;=prime[y] prime[z]>=prime[y],那么这个 i = p r i m e [ z ] e × t i=prime[z]^e\times t i=prime[z]e×t,其中 t t t的最小质因子 &gt; p r i m e [ z ] &gt;prime[z] >prime[z](注意是大于)。

这样枚举 z z z e ≥ 1 e\ge1 e1使得 p r i m e [ z ] e + 1 ≤ x prime[z]^{e+1}\le x prime[z]e+1x,然后答案加上 S ( x / ( p r i m e [ z ] e ) , z + 1 ) × F ( p r i m e [ z ] e ) + F ( p r i m e [ z ] e + 1 ) S(x/(prime[z]^e),z+1)\times F(prime[z]^e)+F(prime[z]^{e+1}) S(x/(prime[z]e),z+1)×F(prime[z]e)+F(prime[z]e+1)(后面那个是去计算 t = 1 t=1 t=1的情况,由于 F ( p r i m e [ z ] ) F(prime[z]) F(prime[z])已经在之前的 g g g函数中处理过了,所以这里就不用算了)。

注意到这里至少是 p r i m e [ z ] 2 ≤ x prime[z]^2\le x prime[z]2x,因此只要枚举到根号 x x x的质数即可。
这里一个小细节是为啥没有加上 S ( x / ( p r i m e [ z ] e + 1 ) , z + 1 ) × F ( p r i m e [ z ] e + 1 ) S(x/(prime[z]^{e+1}),z+1)\times F(prime[z]^{e+1}) S(x/(prime[z]e+1),z+1)×F(prime[z]e+1) e e e取到最大),这是因为 p r i m e [ z ] prime[z] prime[z] e + 2 e+2 e+2次是超过了 n n n的,那么 p r i m e [ z ] prime[z] prime[z] e + 1 e+1 e+1次再乘以后面某个大于 p r i m e [ z ] prime[z] prime[z]的质数也就爆炸了)。
……终于说完了。这东西复杂度为啥是对的我也不知道,反正跑的还挺快就是了。
代码是divcntk的代码,满足 F ( p ) = k + 1 F(p)=k+1 F(p)=k+1(是个零次多项式)。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ull unsigned long long
#define MXS 100010
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int notp[MXS],pri[MXS];
ull s[MXS],l[MXS];
inline int my_sqrt(ull n)
{
	int x=(int)sqrt(n);
	while(1ull*x*x<n) x++;
	while(1ull*x*x>n) x--;
	return x;
}
inline int prelude(int n)
{
	notp[1]=1;int pc=0;
	for(int i=2;i<=n;i++) if(!notp[i])
	{
		pri[++pc]=i;
		for(int j=i;(ull)j*i<=(ull)n;j++)
			notp[i*j]=1;
	}
	return pri[++pc]=n+1;
}
inline int get_g(ull n,int sn,ull k)
{
	for(int i=1;i<=sn;i++) s[i]=i,l[i]=n/i;
	for(int p=2;p<=sn;p++)
	{
		if(notp[p]) continue;ull r=(ull)p*p,v=s[p-1];
		int ed1=sn/p,ed2=(int)min(n/r,(ull)sn);
		for(int i=1;i<=ed1;i++) l[i]-=l[i*p]-v;
		for(int i=ed1+1;i<=ed2;i++) l[i]-=s[n/i/p]-v;
		for(int i=sn;(ull)i>=r;i--) s[i]-=s[i/p]-v;
	}
	for(int i=1;i<=sn;i++) s[i]*=(k+1),l[i]*=(k+1);
	return 0;
}
inline ull F(ull n,ull x,int y,int sn,ull k)
{
	if((ull)pri[y]>x) return 0;ull res=-s[pri[y]-1];
	if(x<=(ull)sn) res+=s[x];else res+=l[n/x];
	for(int i=y;(ull)pri[i]*pri[i]<=x;i++)
	{
		ull v=pri[i];
		for(int j=1;v*pri[i]<=x;j++,v*=pri[i])
			res+=F(n,x/v,i+1,sn,k)*(j*k+1)+(j+1)*k+1;
	}
	return res;
}
int main()
{
	ull n,k;int sn;scanf("%llu%llu",&n,&k);
	sn=my_sqrt(n),prelude(sn),get_g(n,sn,k);
	return !printf("%llu\n",F(n,n,1,sn,k)+1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值