min_25 筛

· · 个人记录

大概只会用到素数个数

作用

假设有一个积性函数 f(x),满足 f(p^k) 是关于 p 的多项式。

则 min_25 可以用来求 f 的前缀和。(单点求值)

过程

min_25 主要思想是把质数和合数分开考虑,对于合数考虑枚举最小质因子进行递归处理。

显然 S(n) 可以分为质数的 f 和合数的 f 加起来,分为两个部分处理。

第一部分

p_i 表示从小到大第 i 个质数,minp(x) 表示 x 的最小质因子。

由于 f(p) 是一个多项式,我们考虑将每一项拿出来单独处理。(比如说 f(p)=p+p^2,就分别计算 pp^2 的和再加起来)

所以这里考虑一般情况,设 h(x)=x^k,显然 h 是一个完全积性函数。

设一个 dp。

g(n,i)=\sum_{x\le n}h(x)[(x\ is\ prime)\ or\ (minp(x)>p_i)]

那么 g(n,\infty) 就表示 n 以内所有质数对 f 的贡献。

实际上第二维只需要处理到 \le \sqrt n 的质数,因为如果是合数就至少包含两个质因子,所以小的那个肯定小于根号。

考虑从 g(n,i-1) 推到 g(n,i),减少的是那些最小质因子为 p_i 的合数,也就是:

g(n,i) = g(n,i-1)-\sum_{x\le n}[(x\ is\ not\ prime)\ and\ (minp(x)=p_i)]

但是 minp(x)=p_i 的限制不是很好弄,这里 h 是完全积性函数的好处就体现出来了,h(x)=h(\frac x{p_i})\cdot h(p_i)

所以我们考虑将 x 除掉一个 p_i,将限制变为 minp(\frac x{p_i})\ge p_i,然后可以直接乘上 h(p_i)

g(n,i) = g(n,i-1)-h(p_i)\cdot\sum_{x\le\lfloor\frac n{p_i}\rfloor}[minp(x)\ge p_i] 假设 $sp(x)$ 表示 $x$ 以内所有质数的 $h$ 的和。 则 $g(n,i)=g(n,i-1)-h(p_i)\cdot(g(\lfloor\frac n{p_i}\rfloor,i-1)-sp(p_{i-1}))

初始化也就是 g(n,0),相当于就是 \sum_{i=1}^nh(i)。(可以理解为 p_0=1

显然会进行 O(\text{多项式次数}) 次第一部分,为了方便后面描述,令 H(n) 表示 n 以内所有质数的 f 的和。

实际上根据过程,我们可以得到所有 H(\lfloor\frac nx\rfloor) 的值。

第二部分

类似于第一部分,设一个dp:

S(n,i)=\sum_{x\le n}f(x)[(x\ is\ prime)\ or\ (minp(x)\ge p_i)]

注意区别,第一部分是对 h 求和,这里是对 f 求和,而 f 只是积性函数,并不是完全积性函数,所以不能套用做法

为了方便,我们令 p_0=1,那答案就是 S(n,0)

考虑如何求 S(n,i),分为质数和合数两部分,对于合数部分枚举最小质因子以及次数。

S(n,i)=H(n)+\sum_{p_j>p_i,k}f({p_j}^k)\cdot( S(\lfloor\frac n{p_j}\rfloor,k)+[k>1])

后面那个 [k>1] 是因为 f(p^k) 也要做出贡献,而 p^k 除掉 p^k 只有 1 了,但是 S(\lfloor\frac n{p_j}\rfloor,k) 里面没有包含 1,而 >1 是因为 p^1 贡献放在质数里算了。

## 一些细节 对于第一部分,注意到我们只需要 $\lfloor\frac ni\rfloor$ 处的 $dp$ 值,所以只用存根号个状态,具体来讲就是要重分配标号,对于小于根号的标号就记录到 $id1[x]$,大于根号的记到 $id2[n/x]$ 里面。 `inline int getpos(ll x){return x<=B?id1[x]:id2[n/x];}` 我的写法是没有计算 $1$ 的贡献的,所以最后要加上。 ## 想法 注意到 $f(p)$ 只要是能拆成能快速求前缀和的若干完全积性函数 $h(p)$ 就能使用 min_25,所以可能能维护矩阵(? ## 模板 [题目](https://www.luogu.com.cn/problem/P5325) ```cpp #include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar()))if(ch=='-')flag=true; for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48)); if(flag)n=-n; } typedef long long ll; const int MOD = 1000000007; const int inv6 = (MOD+1)/6; const int inv2 = (MOD+1)/2; enum{ MAXN = 2000005 }; inline int inc(int a, int b){a+=b;if(a>=MOD)a-=MOD;return a;} inline int dec(int a, int b){a-=b;if(a<0)a+=MOD;return a;} inline void iinc(int &a, int b){a=inc(a,b);} inline void ddec(int &a, int b){a=dec(a,b);} inline void upd(int &a, ll b){a=(a+b)%MOD;} ll n; int B, id1[MAXN], id2[MAXN], num; ll val[MAXN]; inline int getpos(ll x){return x<=B?id1[x]:id2[n/x];} int prime[MAXN], pnum, s1[MAXN], s2[MAXN]; char check[MAXN]; inline void Sieve(int n){ check[1] = true; for(int i=2; i<=n; i++){ if(!check[i]) prime[++pnum] = i, s1[i] = i, s2[i] = 1ll*i*i%MOD; for(int j=1; j<=pnum and i*prime[j]<=n; j++){ check[i*prime[j]] = true; if(i%prime[j]==0) break; } } for(int i=1; i<=n; i++) iinc(s1[i],s1[i-1]), iinc(s2[i],s2[i-1]); } inline int sx2(ll n){n%=MOD;return n*(n+1)%MOD*(2*n+1)%MOD*inv6%MOD;} inline int sx(ll n){n%=MOD;return n*(n+1)%MOD*inv2%MOD;} int g1[MAXN], g2[MAXN]; int solve(int idn, int idp){ // printf("solve %d %d\n",val[idn],prime[idp]); int P = prime[idp]; ll n = val[idn]; if(P>=n) return 0; int res=dec(dec(g2[idn],g1[idn]),dec(s2[P],s1[P])); for(int i=idp+1; i<=pnum and 1ll*prime[i]*prime[i]<=n; i++){ int p = prime[i], sqr = 1ll*p*p%MOD, t1 = sqr, t2 = p; ll cur = p; while(n/p >= cur){ upd(res,1ll*dec(t1,t2)*solve(getpos(n/cur),i)); t1 = 1ll*sqr*t1%MOD, t2 = 1ll*p*t2%MOD, cur *= p; iinc(res,dec(t1,t2)); } } return res; } int main(){ Read(n); B = sqrt(n); Sieve(B); for(ll l=1; l<=n; l=n/(n/l)+1){ ll x = n/l; val[++num] = x; if(x<=B) id1[x] = num; else id2[n/x] = num; } for(int i=1; i<=num; i++) g1[i] = dec(sx(val[i]),1), g2[i] = dec(sx2(val[i]),1); for(int i=1; i<=pnum; i++) for(int j=1; j<=num and 1ll*prime[i]*prime[i]<=val[j]; j++) ddec(g2[j],1ll*prime[i]*prime[i]%MOD*dec(g2[getpos(val[j]/prime[i])],s2[prime[i-1]])%MOD), ddec(g1[j],1ll*prime[i]*dec(g1[getpos(val[j]/prime[i])],s1[prime[i-1]])%MOD); prime[0] = 1; cout<<inc(solve(1,0),1)<<'\n'; return 0; } ```