Min_25筛

积性函数的前缀和

简介

求积性函数前缀和,线性筛需要把函数的每一位值都算出来,作了许多不必要的操作。
线性筛中,通过计算最小质因子幂的函数值,与之前已计算出的函数值相乘,得到新的函数值。
如果我们能批量执行上面的操作,即用最小质因子幂的函数值,与另一些函数值的和相乘,就可以得到更多的函数值的和。
我们还能发现,当我们要求前 n n n项的和时,大于 n \sqrt n n 的质因子都不能用于计算任何其它的积性函数

于是就有了Min_25筛,将质数分为小于等于 n \sqrt n n ,和大于 n \sqrt n n 的。再预处理出所有质数函数值的前缀和,利用小于等于 n \sqrt n n 的质数进行转移,推出所有的函数值的和。

用途

对于积性函数 F ( n ) F(n) F(n)
O ( n 3 4 l o g   n ) O(\frac {n^{\frac 3 4}} {log\ n}) O(log nn43)的复杂度内求出 ∑ i = 1 n F ( i ) \sum_{i=1}^n F(i) i=1nF(i)
要求对于每个质数 p p p F ( p ) F(p) F(p)是简单的多项式, F ( p k ) F(p^k) F(pk)很容易算出。

一般题目 n = 1 0 10 n=10^{10} n=1010左右的数量级

过程

预处理

我们需要得到所有质数函数值得前缀和。

首先用线性筛处理出 ≤ n \leq \sqrt n n 的质数, p r [ i ] pr[i] pr[i]表示第i个质数。

设原积性函数为 F ( n ) F(n) F(n),即满足若 ( a , b ) = 1 (a,b)=1 (a,b)=1,则 F ( a b ) = F ( a ) F ( b ) F(ab)=F(a)F(b) F(ab)=F(a)F(b)
p p p为质数, F ( p ) = f 0 ( p ) + f 1 ( p ) + . . . F(p)=f_0(p)+f_1(p)+... F(p)=f0(p)+f1(p)+...(必须保证 f ( p ) = p k f(p)=p^k f(p)=pk)。
即质数代入原函数中,得到的简单多项式,每一项都拆成一个函数 f f f,下面的过程将对每一个 f f f单独求和。
*注:即使p不是质数,也可以代入f(p)进行计算,但这并不是原来的积性函数的值。
g ( i , j ) g(i,j) g(i,j)满足 g ( i , j ) = ∑ k 为 质 数 , 或 k 的 最 小 质 因 子 > p r [ j ] i f ( k ) g(i,j)=\sum_{k为质数,或k的最小质因子> pr[j]}^i f(k) g(i,j)=kk>pr[j]if(k)
实际上可以发现,上式中的k,即为线性筛筛了j个质数后,剩余没有被筛掉的数。
因为线性筛用 ≤ n \leq \sqrt n n 的质数就可以筛掉 n n n以内的合数,所以我们只预处理了 ≤ n \leq \sqrt n n 的质数。( g ( n , ∣ p r ∣ ) g(n,|pr|) g(n,pr)即为n以内所有质数的函数值之和,|pr|为 n \sqrt n n 以内的质数个数 )

g ( i , j ) g(i,j) g(i,j)的转移:

p r [ j ] 2 > i pr[j]^2>i pr[j]2>i时,显然线性筛中 p r [ j ] pr[j] pr[j]筛不掉任何数,所以 g ( i , j ) = g ( i , j − 1 ) g(i,j)=g(i,j-1) g(i,j)=g(i,j1)
p r [ j ] 2 ≤ i pr[j]^2\leq i pr[j]2i时,将上一次的答案减去 p r [ j ] pr[j] pr[j]筛掉的答案,即最小质因子为 p r [ j ] pr[j] pr[j]的合数的函数值之和,将这些数的 p r [ j ] pr[j] pr[j]提出来,剩下的和可以用g表示,公式如下: g ( i , j ) = g ( i , j − 1 ) − f ( p r [ j ] ) ( g ( ⌊ i p r [ j ] ⌋ , j − 1 ) − ∑ k = 1 j − 1 f ( p r [ k ] ) ) g(i,j)=g(i,j-1)-f(pr[j])\left(g\left(\left \lfloor \frac i {pr[j]} \right \rfloor ,j-1\right)-\sum_{k=1}^{j-1}f(pr[k])\right) g(i,j)=g(i,j1)f(pr[j])(g(pr[j]i,j1)k=1j1f(pr[k]))
初始状态 g ( i , 0 ) g(i,0) g(i,0) ∑ k = 2 i f ( k ) \sum_{k=2}^if(k) k=2if(k),不管k是不是质数。。( f ( 1 ) f(1) f(1)需单独处理)

在实现过程中,可以发现最后 g ( i , j ) g(i,j) g(i,j)只需要用到 i = ⌊ n k ⌋ i=\lfloor \frac n k \rfloor i=kn, j = ∣ p r ∣ j=|pr| j=pr的部分,总共只有 2 n 2\sqrt n 2n 个位置,可以离散化处理,后面再讲。

递归求和

我们现在已经可以通过 g ( i , ∣ p r ∣ ) g(i,|pr|) g(i,pr)得到所有质数的函数值前缀和,现在要把它扩展到所有数。
S ( i , j ) S(i,j) S(i,j),满足 S ( i , j ) = ∑ k 的 最 小 质 因 子 ≥ p r [ j ] i F ( k ) S(i,j)=\sum_{k的最小质因子\geq pr[j]}^iF(k) S(i,j)=kpr[j]iF(k)
如果 i &lt; p r [ j ] i&lt;pr[j] i<pr[j],显然 S ( i , j ) = 0 S(i,j)=0 S(i,j)=0,现在考虑当 i ≥ p r [ j ] i\geq pr[j] ipr[j]
S ( i , j ) S(i,j) S(i,j)中质数部分很容易算出,为 g ( i , ∣ p r ∣ ) − ∑ k = 1 j − 1 f ( p r [ k ] ) g(i,|pr|)-\sum_{k=1}^{j-1}f(pr[k]) g(i,pr)k=1j1f(pr[k])
合数部分,可以枚举合数的最小质因子,和最小质因子的指数,将规模缩小,递归求解。
S ( i , j ) = g ( i , ∣ p r ∣ ) − ∑ k = 1 j − 1 f ( p r [ k ] ) + ∑ k = j p r [ k ] 2 ≤ i ∑ e = 1 p r [ k ] e + 1 ≤ i F ( p r [ k ] e ) × S ( ⌊ i p r [ k ] e ⌋ , k + 1 ) + F ( p r [ k ] e + 1 ) S(i,j)=g(i,|pr|)-\sum_{k=1}^{j-1}f(pr[k])+\sum_{k=j}^{pr[k]^2\leq i}\sum_{e=1}^{pr[k]^{e+1}\leq i}F(pr[k]^e)\times S\left(\left\lfloor \frac i {pr[k]^e}\right\rfloor,k+1\right)+F(pr[k]^{e+1}) S(i,j)=g(i,pr)k=1j1f(pr[k])+k=jpr[k]2ie=1pr[k]e+1iF(pr[k]e)×S(pr[k]ei,k+1)+F(pr[k]e+1)
末状态为 S ( 1 , j ) = 0 S(1,j)=0 S(1,j)=0,和 S ( i , j ) = 0   ( p r [ j ] &gt; i ) S(i,j)=0\ (pr[j]&gt;i) S(i,j)=0 (pr[j]>i)
*注:因为实际上F(n)被拆成了多个fi函数,实际上式中对f(pr[k])求和部分要将每一个fi函数的值求和

结果

∑ i = 1 n F ( i ) = S ( n , 1 ) + F ( 1 ) \sum_{i=1}^n F(i)=S(n,1)+F(1) i=1nF(i)=S(n,1)+F(1)

实现

观察发现我们要求的是 S ( n , 1 ) S(n,1) S(n,1),里面只会访问到 g ( n , ∣ p r ∣ ) g(n,|pr|) g(n,pr) g ( ⌊ n a 1 ⌋ , ∣ p r ∣ ) g(\left\lfloor \frac n {a_1} \right\rfloor,|pr|) g(a1n,pr) g ( ⌊ n a 1 a 2 ⌋ , ∣ p r ∣ ) g(\left\lfloor \frac {\frac n {a_1}} {a_2} \right\rfloor,|pr|) g(a2a1n,pr)
也就是实际上用到的值只有 g ( ⌊ n i ⌋ , ∣ p r ∣ ) g(\left\lfloor \frac n i \right\rfloor,|pr|) g(in,pr),只有最多 2 n 2\sqrt n 2n 个位置,预处理出这些位置,离散化如果 ⌊ n i ⌋ ≤ n \left\lfloor \frac n i \right\rfloor \leq \sqrt n inn i d 1 [ ⌊ n i ⌋ ] id1[\left\lfloor \frac n i \right\rfloor] id1[in]存储离散化之后的下标,如果 ⌊ n i ⌋ &gt; n \left\lfloor \frac n i \right\rfloor&gt;\sqrt n in>n i d 2 [ ⌊ n ⌊ n i ⌋ ⌋ ] id2[\left\lfloor \frac n {\left\lfloor \frac n i \right\rfloor}\right\rfloor] id2[inn]存储它的下标。

因为最后只需要用到 g ( ⌊ n i ⌋ , ∣ p r ∣ ) g(\left\lfloor \frac n i \right\rfloor,|pr|) g(in,pr),所以第二维不需要开数组存储,转移时之间用一维计算即可。

这样空间复杂度就降为了 O ( n ) O(\sqrt n) O(n )

后面求S没什么好说的,直接递归。

时间复杂度

不会证

例题:LOJ6053 简单的函数

定义积性函数 F ( n ) F(n) F(n)
F ( p c ) = p ⊕ c F(p^c)=p\oplus c F(pc)=pc

发现除了2以外的质数, F ( p ) = p − 1 F(p)=p-1 F(p)=p1,即可以把F拆成两个f函数
f 0 ( p ) = p f_0(p)=p f0(p)=p
f 1 ( p ) = 1 f_1(p)=1 f1(p)=1
然后分别求和计算,用 g ( i , j ) g(i,j) g(i,j)表示 f 0 f_0 f0的和, h ( i , j ) h(i,j) h(i,j)表示 f 1 f_1 f1的和

在求 S ( i , j ) S(i,j) S(i,j)时,如果 j = 1 j=1 j=1,将答案+2(用于特判p=2)

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MOD=1000000007,MAXN=200005;

long long n,sqr;

bool npr[MAXN];
int pr[MAXN],sum[MAXN],cnt[MAXN],P;
int id1[MAXN],id2[MAXN],m;
long long w[MAXN];
int g[MAXN],h[MAXN];

void LinerSieve()
{
    npr[1]=true;
    for(int i=2;i<=sqr;i++)
    {
        if(!npr[i])
        {
            pr[++P]=i;
            sum[P]=(sum[P-1]+i)%MOD;
        }
        for(int j=1;j<=P&&1LL*i*pr[j]<=sqr;j++)
        {
            npr[i*pr[j]]=true;
            if(i%pr[j]==0)
                break;
        }
    }
}
void Calc_g_h()
{
    for(long long i=1,j;i<=n;i=j+1)
    {
        j=n/(n/i);
        w[++m]=n/i;
        if(w[m]<=sqr)
            id1[w[m]]=m;
        else
            id2[n/w[m]]=m;
        g[m]=(w[m]%MOD+2)*(w[m]%MOD-1)%MOD*(MOD+1)/2%MOD;
        h[m]=w[m]%MOD-1;
    }
    for(int j=1;j<=P;j++)
        for(int i=1;i<=m&&1LL*pr[j]*pr[j]<=w[i];i++)
        {
            long long k=w[i]/pr[j];
            k=(k<=sqr)?id1[k]:id2[n/k];
            g[i]=(g[i]-1LL*pr[j]*(g[k]-sum[j-1])%MOD+MOD)%MOD;
            h[i]=(h[i]-1LL*(h[k]-(j-1))%MOD+MOD)%MOD;
        }
}
int S(long long x,int j)
{
    if(x<=1||pr[j]>x)
        return 0;
    int k=(x<=sqr)?id1[x]:id2[n/x];
    int res=((g[k]-h[k]-sum[j-1]+(j-1))%MOD+MOD)%MOD;
    if(j==1)
        res=(res+2)%MOD;
    for(int k=j;k<=P&&1LL*pr[k]*pr[k]<=x;k++)
    {
        long long t=pr[k];
        for(int e=1;1LL*pr[k]*t<=x;e++,t*=pr[k])
            res=((res+1LL*(pr[k]^e)*S(x/t,k+1)%MOD)%MOD+(pr[k]^(e+1)))%MOD;
    }
    return res;
}

int main()
{
    scanf("%lld",&n);
    sqr=sqrt(n);
    LinerSieve();
    Calc_g_h();
    int ans=S(n,1)+1;
    printf("%d\n",ans);

    return 0;
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值