Min_25 筛
简介
Min_25 可以快速解决一类积性函数的前缀和问题。
概述
求积性函数 的前缀和。当然,在《从掌握到精通》中也有提到,其实可以拓展到部分非积性函数。
总的来说,算法第一步,求出质数 对应的 的前缀和。第二步,求出 的前缀和。从某种意义上说,这是因为质数的幂的数量很小,可以暴搜合数的质因数分解;但质数处的取值就很难搞。
Step One
一般的积性函数是复杂的。所以我们需要用一个 完全积性函数 去 拟合 原函数。只需要让 保持不变即可,因为 只需要求出 。
用 表示 去除前 个质数的倍数之后,剩余数字的 之和。注意:要求 时刻包含 之间所有质数的 ,也就是说,去除的是质数的 “真倍数”(不包含质数本身)。我们的目标是求出 ,也就是只留下了质数。
设第 大的质数是 。转移过程就是去掉 即可。显然 不应该是前 个质数之一的倍数(含自身),因为那是已经被移除的数。又因为 是完全积性函数,有 ,于是有转移式
这里 ,因为 里面没有挖掉质数本身,减去它以修正。
为何有范围限制呢?如果 ,那么显然没有更多的数字需要被筛掉,此时 。若在 这一维上做滚动数组,则无需修改这些位置。同时可知,需要的质数最大是 ,所以直接线性筛即可求出 了。
我们要对 赋初值。所以又涉及一个问题是,拟合函数的前缀和要易于求解。一般来说,我们会选择单项式(如果是多项式,就单项式分别做 然后相加)作为拟合函数。
而 的范围仍然是 ,这可不行。注意到我们只需要,所以 只会递归到形如 的地方。只对这样的 求解 就行啦!具体实现的时候,可以先预处理出所有这样的 ,放在数组中;无需 从值映射到下标,因为这些值的特点是 互不相同,所以 时就放在某个长度为 的静态数组里即可(用 作为下标)。
Step Two
注意: 总是会被算漏。记得最后加上。
递归法
我们前面只求出了质数的求和是 ,所以现在我们只需要考虑合数了。
好消息:合数的最小质因数是不超过 的!于是咱可以枚举这玩意儿。记 的最小质因数为 。
记 。那么答案就是 ,求出它就完事儿了!
枚举最小质因子与其幂次,可以写出转移式
用 计算了 中的质数 ,用 计算了含至少两个质因子的合数,用 计算了只含一个质因子的合数,可谓不重不漏。
有一个小细节:时, 这一项是 ,可以略去。所以递归时要求 。而这个范围恰好使得 就是所有只含一个质因子的合数!于是代码就比较简单。
同理, 才可以转移。有趣的是,我们可以直接 递归,而且 不进行记忆化。据称,在 可接受范围内,这种方法优于 。详见文末。
递推法
显然我们也有
初值应当为 ,其中 是大于 的最小质数。反正最后都要计算,干脆就保持 为大于 的最小质数进行赋值吧。
处理则稍微麻烦些:对于 ,正常转移。对于 ,可以发现右侧的 函数值为 ,提供贡献的只有那个 了,也就是较大的质数处的 值;用 中求出的 算一下就好。
这样的处理很麻烦。不如学习一下 的思想,令 始终包含全体质数!那么转移式就应该写为
那么 时已经无需转移了!初值则设置为 ,相当于在 的基础上接着递推,结束。
【模板】Min_25筛
定义积性函数,且(是一个质数),求
对取模。
对于的数据,保证
代码
时空复杂度 In
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MOD = 1e9+7, SQRTN = 100005;
int primes[SQRTN], primes_size;
bool isPrime[SQRTN];
struct Node{
int one, two;
Node() = default;
Node(int x):one(x),two(int(llong(x)*x%MOD)){}
Node(int _o,int _t):one(_o),two(_t){}
Node operator * (const Node &t) const {
return Node(int(llong(one)*t.one%MOD),
int(llong(two)*t.two%MOD));
}
Node operator - (const Node &t) const {
return Node((one-t.one+MOD)%MOD,(two-t.two+MOD)%MOD);
}
int val() const { return (two-one+MOD)%MOD; }
};
Node sumf[SQRTN];
void sieve(int n){
memset(isPrime+2,true,n-1);
for(int i=2; i<=n; ++i){
if(isPrime[i]){
primes[++ primes_size] = i;
Node &v = sumf[primes_size] = sumf[primes_size-1];
if((v.one += i) >= MOD) v.one -= MOD;
v.two = int((v.two+llong(i)*i)%MOD);
}
for(int j=1; j<=primes_size&&primes[j]<=n/i; ++j){
isPrime[i*primes[j]] = false;
if(i%primes[j] == 0) break;
}
}
}
const int inv2 = (MOD+1)>>1, inv3 = (MOD+1)/3;
int haxi[2][SQRTN]; // value or divisor
inline int& index_(const llong &x,const llong &n){
return (x < SQRTN) ? haxi[0][x] : haxi[1][n/x];
}
llong w[SQRTN<<1]; ///< positions to get sum
Node g[SQRTN<<1]; ///< 2 times SQRTN
void step_one(const llong &n){
int tot = 0; ///< allocate index
for(llong i=1; i<=n; i=n/(n/i)+1){
w[++ tot] = n/i; index_(n/i,n) = tot;
g[tot].one = int(((w[tot]%MOD+1)*(w[tot]%MOD)>>1)%MOD)-1;
g[tot].two = int((w[tot]<<1|1)%MOD*(w[tot]%MOD+1)
%MOD*(w[tot]%MOD)%MOD*inv2%MOD*inv3%MOD)-1;
}
for(int j=1; j<=primes_size; ++j){
if(primes[j] > n/primes[j]) break;
for(int i=1; i<=tot; ++i){
if(primes[j] > w[i]/primes[j]) break;
g[i] = g[i]-Node(primes[j])*(
g[index_(w[i]/primes[j],n)]-sumf[j-1]);
}
}
}
inline llong func(const llong &x){
return (x-1)%MOD*(x%MOD)%MOD; // definition
}
int step_two(const llong &x,int i,const llong &n){
if(primes[i] > x) return 0;
int res = g[index_(x,n)].val()-sumf[i-1].val();
for(; i<=primes_size&&primes[i]<=x/primes[i]; ++i){
llong t = primes[i], fk = x/t;
for(; t<=fk; t*=primes[i])
res = int((func(t*primes[i])+func(t)
*step_two(x/t,i+1,n)+res)%MOD);
}
return (res >= 0) ? res : (res+MOD);
}
int main(){
sieve(SQRTN-1); // just do it
llong n; scanf("%lld",&n); step_one(n);
printf("%d\n",step_two(n,1,n)+1);
return 0;
}
更多内容
关于 Min_25
已经有更快的算法了,详见此博客。
本文作者:PassName
本文链接:https://www.cnblogs.com/spaceswalker/p/16293882.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步