min_25筛

笔记写在前面:
m i n 25 min25 min25 筛是用来求积性函数前缀和 ∑ i = 1 n f ( i ) \sum_{i=1}^nf(i) i=1nf(i) 的一个筛法,其时间复杂度为 O ( n 3 4 log ⁡ n ) O(\frac{n^{\frac 34}}{\log n}) O(lognn43) 大约 1 s 1s 1s 能跑 1 e 10 1e10 1e10
该积性函数需要满足:

  1. f ( p ) f(p) f(p) 是一个关于质数 p p p 的项数较小的多项式 或者 能快速求值
  2. f ( p k ) f(p^k) f(pk) 可以快速求值

我们把 f ( i ) f(i) f(i) i i i 是否是质数分为两组,分别计算:
对于质数部分:
我们定义一个完全积性函数 F ( i ) F(i) F(i) (我们不需要关注其具体形式),该函数满足 F ( p ) = f ( p ) F(p)=f(p) F(p)=f(p) (其中 p p p 是一个质数)。
定义一个神仙 d p dp dp
g ( n , j ) = ∑ i = 1 n F ( i ) [ i 是 质 数   或   i 的 最 小 质 因 子 大 于 第 j 个 质 数 ] g(n,j)=\sum_{i=1}^nF(i)[i是质数\ 或\ i的最小质因子大于第j个质数] g(n,j)=i=1nF(i)[i  ij]
注意: j j j 趋于无穷,即 g ( n , = + ∞ ) g(n,=+\infty) g(n,=+) 时,有 ∑ i = 1 n F ( i ) = ∑ i = 1 n f ( i ) \sum_{i=1}^nF(i)=\sum_{i=1}^nf(i) i=1nF(i)=i=1nf(i)
接下来考虑 g ( n , j ) g(n,j) g(n,j) 的转移方程
设第 j j j 个质数为 p j p_j pj
p j > n p_j>\sqrt n pj>n ,即( p j 2 > n p^2_j>n pj2>n)时:
我们发现若 p j p_j pj 是一个质数的最小质因子且这个数要被 g ( n , j ) g(n,j) g(n,j) 计算,他至少是 p j 2 p_j^2 pj2 ,而 p j 2 > n p^2_j > n pj2>n 所以此时有 g ( n , j ) = g ( n , j − 1 ) g(n,j)=g(n,j-1) g(n,j)=g(n,j1)
p j ≤ n p_j\le \sqrt n pjn ,即( p j 2 ≤ n p^2_j\le n pj2n)时:
不难发现,从 g ( n , j − 1 ) g(n,j-1) g(n,j1) 转移到 g ( n , j ) g(n,j) g(n,j) 时值是必然减小的,其减小的值就是最小质因子为 p j p_j pj 的合数的值(因为 g ( n , j − 1 ) g(n,j-1) g(n,j1) 统计的所有数的最小质因子 m i n p > j − 1 min_p > j-1 minp>j1 g ( n , j ) g(n,j) g(n,j) 统计的数字的最小质因子 m i n p > j min_p>j minp>j ,所以被减去的就是最小质因子为 p j p_j pj 的合数。注意 p j p_j pj 是质数一定会被计算,所以需要被保留下来)
我们不妨设这些合数为 x x x ,他们的函数值为 F ( x ) F(x) F(x) ,由 F ( x ) F(x) F(x) 是完全积性函数可得 F ( x ) = F ( p j ) ⋅ F ( x p j ) F(x)=F(p_j)·F(\frac x{p_j}) F(x)=F(pj)F(pjx)
其中 F ( x p j ) F(\frac x{p_j}) F(pjx) 是可以用 g ( ⌊ n p j ⌋ , j − 1 ) − ∑ i = 1 j − 1 F ( p j ) g(\lfloor \frac{n}{p_j} \rfloor,j-1)-\sum_{i=1}^{j-1}F(p_j) g(pjn,j1)i=1j1F(pj) 表示的(这里如果不明白,可以自己手写几个数字模拟一下)
综上我们就有了 g ( n , j ) g(n,j) g(n,j) 的转移方程:
g ( n , j ) = { g ( n , j − 1 ) p j 2 > n g ( n , j − 1 ) − F ( p j ) ⋅ ( g ( ⌊ n p j ⌋ , j − 1 ) − ∑ i = 1 j − 1 F ( p j ) ) p j 2 ≤ n g(n,j)= \begin{cases} g(n,j-1) & {p_j^2>n}\\ g(n,j-1)-F(p_j)·(g(\lfloor \frac{n}{p_j} \rfloor,j-1)-\sum_{i=1}^{j-1}F(p_j)) & {p_j^2\le n}\\ \end{cases} g(n,j)={g(n,j1)g(n,j1)F(pj)(g(pjn,j1)i=1j1F(pj))pj2>npj2n

现在我们来加上合部分来计算整体答案:
我们引入一个新的没那么神仙 d p dp dp 状态:
S ( n , j ) = ∑ i = 1 n f ( i ) [ i 的 最 小 质 因 子 大 于 第 j 个 质 数 ] S(n,j)=\sum_{i=1}^nf(i)[i的最小质因子大于第j个质数] S(n,j)=i=1nf(i)[ij]
我们最终的答案就是 S ( n , 0 ) + 1 S(n,0)+1 S(n,0)+1 (注意 f ( i ) f(i) f(i) 是一个积性函数)

接下来考虑如何计算 S ( n , j ) S(n,j) S(n,j),其可以利用 g ( n , j ) g(n,j) g(n,j) 来得到一个方程,如下:
S ( n , j ) = g ( n , ∣ P ∣ ) − ∑ i = 1 j − 1 f ( p i ) + ∑ k = j + 1 p k 2 ≤ n ∑ e = 1 p k e ≤ n f ( p k e ) ( S ( ⌊ n p k e ⌋ , k ) + [ e > 1 ] ) S(n,j)=g(n,|P|)-\sum_{i=1}^{j-1}f(p_i)+\sum_{k=j+1}^{p_k^2\le n}\sum_{e=1}^{p^e_k\le n}f(p_k^e)(S(\lfloor \frac{n}{p_k^e} \rfloor,k)+[e>1]) S(n,j)=g(n,P)i=1j1f(pi)+k=j+1pk2ne=1pkenf(pke)(S(pken,k)+[e>1])
其中 ∣ P ∣ |P| P 表示 n \sqrt n n 以内的质数个数(这里我们用到的 f f f S S S 拆开是积性函数的性质)
这个递归方程的时间复杂度被证明是 O ( n 3 4 log ⁡ n ) O(\frac{n^{\frac 34}}{\log n}) O(lognn43)

g ( n , ∣ P ∣ ) − ∑ i = 1 j − 1 f ( p i ) g(n,|P|)-\sum_{i=1}^{j-1}f(p_i) g(n,P)i=1j1f(pi) 就是质数的贡献:
g ( n , ∣ P ∣ ) g(n,|P|) g(n,P) 只保留了 n n n 以内的质数和,因为 n n n 以内没有最小质因子大于 n \sqrt n n 的数字
S ( n , ∣ p ∣ ) S(n,|p|) S(n,p) 求的是最小质因子大于第 j j j 个质数的数的和,所以需要减去前 j − 1 j-1 j1 个质数对应函数值的前缀和
接下来考虑合数的贡献:
因为 S ( n , j ) S(n,j) S(n,j) 是最小质因子大于第 j j j 个质数的函数值的前缀和,所以我们从第 j + 1 j+1 j+1 个质数开始枚举一个合数的最小质因子
因为一个数可能是同一个质数的多次幂,所以需要枚举质数的幂次,依次 p k 2 , p k 3 , . . . , p k e p_k^2,p_k^3,...,p_k^e pk2,pk3,...,pke 直到 p k e ≤ n p_k^e\le n pken
有可能一个数只是这个质数的次方,他不会被 S S S 计算,而且 e e e 应该大于 1 1 1 ,否则他会算上这个质数
这样的质数枚举到 p k 2 ≤ n p_k^2 \le n pk2n 即可

一些技巧与启发

  1. 对于质数部分, m i n 25 min25 min25 筛每次将合数的最小质因子筛去. 我们可以利用这个性质处理一些有关最小质因子的问题
  2. 对于合数部分,我们每次从小到大枚举质因子,我们可以对于枚举的质因子加上一些约束(注意同时修改质数部分),也可以对最大质因子,次大质因子进行计算(UOJ188)
  3. 筛数时可以考虑每个数的最小质因子,因为合数的最小质因子是 n \sqrt n n

题目链接:点击这里

题目大意:
定义积性函数 f ( x ) f(x) f(x) ,且 f ( p k ) = p k ( p k − 1 ) f(p^k)=p^k(p^k-1) f(pk)=pk(pk1) (其中 p p p 是一个质数),现给出一个 n n n 求:
∑ i = 1 n f ( i ) m o d    1 e 9 + 7 \sum_{i=1}^nf(i) \mod 1e9+7 i=1nf(i)mod1e9+7

题目分析:
根据上述两个递推公式我们需要预处理:

  1. ∑ i = 1 j − 1 F ( p i ) \sum_{i=1}^{j-1} F(p_i) i=1j1F(pi) F F F 函数的质数前缀和,注意我们无需构造 F F F ,用 f f f 代替一下即可
  2. n n n 的整除分块的所有可能
  3. g ( n , 0 ) g(n,0) g(n,0) 递推起点

对于预处理 1 1 1
我们发现 f ( p 1 ) = p 1 ( p 1 − 1 ) = p 2 − p f(p^1)=p^1(p^1-1)=p^{2}-p f(p1)=p1(p11)=p2p ,所以我们把 p p p p 2 p^2 p2 分别求前缀和 s p 1 [ ] , s p 2 [ ] sp1[],sp2[] sp1[],sp2[]
而且,由于我们取的 + ∞ +\infty + n \sqrt n n ,所以我们可以在线筛时顺便求出 s p 1 [ ] , s p 2 [ ] sp1[],sp2[] sp1[],sp2[]

对于预处理 2 2 2
使用整除分块时,对于一个数 n n n ,它的整除分块的值约有 n \sqrt n n 个,我们用 w [ ] w[] w[] 保存这些值, t o t tot tot 范围是 [ 1 , t o t ] [1,tot] [1,tot] ,同时用 i d 1 [ ] , i d 2 [ ] id1[],id2[] id1[],id2[] 两个数组保存它们的下标
i d 1 [ ] id1[] id1[] 用来存 n l ≤ n \frac nl \le n lnn 时的下标, i d 2 [ ] id2[] id2[] 用来存 n l > n \frac nl > n ln>n 的下标,由于 n l \frac nl ln 可能很大,所以反过来用 n n l \frac n {\frac nl} lnn 来存 n l \frac nl ln 的下标,这样空间复杂度就在 n \sqrt n n 左右了

对于预处理 3 3 3
在预处理整除分块时我们可以顺便求出 g 1 ( n , 0 ) , g 2 ( n , 0 ) g_1(n,0),g_2(n,0) g1(n,0),g2(n,0)
由于 f 1 ( x ) = x , f 2 ( x ) = x 2 f_1(x) = x,f_2(x)=x^2 f1(x)=x,f2(x)=x2 ,所以这里可以用求和公式 ∑ i = 1 n = n ( n + 1 ) 2 , ∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{i=1}^n=\frac{n(n+1)}2,\sum_{i=1}^ni^2=\frac{n(n+1)(2n+1)}6 i=1n=2n(n+1),i=1ni2=6n(n+1)(2n+1) 直接求出 g 1 ( n , 0 ) , g 2 ( n , 0 ) g_1(n,0),g_2(n,0) g1(n,0),g2(n,0) ,注意减 1 1 1 是因为 1 1 1 不符合条件
然后就可以从 0 0 0 递推到 + ∞ ( n ) +\infty(\sqrt n) +(n )

具体细节见代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
#define ll long long
#define inf 0x3f3f3f3f
#define int  ll
#define endl '\n'
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 1e6+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,cnt,pri[maxn],sp1[maxn],sp2[maxn],sq;
int w[maxn],tot,id1[maxn],id2[maxn],g1[maxn],g2[maxn];
bool vis[maxn];
void get_pri(int n)
{
	for(int i = 2;i <= n;i++)
	{
		if(!vis[i])
		{
			pri[++cnt] = i;
			sp1[cnt] = (sp1[cnt-1]+i)%mod;
			sp2[cnt] = (sp2[cnt-1]+i*i%mod)%mod;
		}
		for(int j = 1;j <= cnt && i*pri[j] <= n;j++)
		{
			vis[i*pri[j]] = true;
			if(i%pri[j] == 0) break;
		}
	}
}
int qpow(int a,int b)
{
	int res = 1;
	while(b)
	{
		if(b&1) res = res*a%mod;
		a = a*a%mod;
		b >>= 1;
	}
	return res;
}
const int inv2 = qpow(2,mod-2),inv6 = qpow(6,mod-2);
ll S(int i,int j)
{
	if(pri[j] >= i) return 0;
	ll pos = i<=sq ? id1[i] : id2[n/i];
	ll res = ((g2[pos]-g1[pos]+mod)%mod-(sp2[j]-sp1[j]+mod)%mod+mod)%mod; //质数部分贡献 
	for(int k = j+1;k<=cnt && pri[k]*pri[k]<=i;k++) //合数部分贡献 
	{
		ll pe = pri[k];
		for(int e = 1;pe <= i;e++,pe = pe*pri[k]) //不能取模 
		{
			ll x = pe%mod;
			res = (res+x*(x-1)%mod*(S(i/pe,k)+(e>1))%mod)%mod;
		} 
	}
	return res;
}
signed main()
{
	n = read(); sq = sqrt(n);
	get_pri(sq);
	for(int l = 1,r;l <= n;l = r+1)
	{
		r = min(n,n/(n/l));
		w[++tot] = n/l%mod; //取余方便计算下述的g(n,0) 
		g1[tot] = (w[tot]*(w[tot]+1)%mod*inv2%mod-1+mod)%mod; //减一是为了减去第一项,因为 1 不是质数 
		g2[tot] = (w[tot]*(w[tot]+1)%mod*(2*w[tot]+1)%mod*inv6%mod-1+mod)%mod;
		w[tot] = n/l;
		if(w[tot] <= sq) id1[w[tot]] = tot;
		else id2[n/w[tot]] = tot;
	}
	for(int j = 1;j <= cnt;j++) //g(n,j)
		for(int i = 1;i<=tot && pri[j]*pri[j]<=w[i];i++)
		{
			ll tmp = w[i]/pri[j];
			ll pos = tmp <= sq ? id1[tmp] : id2[n/tmp];
			g1[i] = (g1[i]-pri[j]*(g1[pos]-sp1[j-1]+mod)%mod+mod)%mod;
			g2[i] = (g2[i]-pri[j]*pri[j]%mod*(g2[pos]-sp2[j-1]+mod)%mod+mod)%mod;
		}
	printf("%lld\n",(S(n,0)+1)%mod);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值