zoukankan      html  css  js  c++  java
  • Min_25筛 学习笔记

    Min_25筛

    一、用途:

    在小于线性的时间内(O(frac{n^{frac 34}}{log n}))计算出积性函数(f(x))的前缀和(sum_{i=1}^nf(i))

    对积性函数(f(x))的要求:(f(p^c)(pin prime))能够快速求值,且(f(p))可以多项式表示,具体原因在之后会体现

    二、一些定义:
    • (P)表示全体素数集合
    • (P_i)表示第(i)个素数,且定义(P_0 = 1)
    • (operatorname{minp}(x))表示(x)的最小素因子
    三、计算方法:

    首先我们用一个方法来计算(sum_{p=2}^nf(p))(pin P)

    定义一个完全积性函数(F(x))(F(x))(xin P)的情况下值和(f(x))相同

    完全积性函数即:对于任意正整数(a,b)满足(F(acdot b)=F(a)cdot F(b))

    • 举个例子:(f(x))为莫比乌斯函数,在质数的情况下(f(p)=-1),那么对于任意数(xge 2)(F(x) = (-1)^{omega(x)})(omega(x))(x)的质因子数量(包括相同的)

    那么我们计算(sum_{p=2}^nf(p))(pin P)就相当于计算(sum_{p=2}^nF(p))(pin P)

    [定义g(m,j)=sum_{i=2}^m F(i)cdot [iin P or operatorname{minp}(i)>P_j] ]

    (g(m,j))等于(x)(2)(m)之间所有满足(x)是质数或者(x)是合数且最小质因子大于第(j)个质数

    考虑转移,存在两种情况:

    1. (P_j^2>m),最小质因子是(P_j)的合数的最小值是(P_j^2),而(P_j^2>m),所以显然合数部分不产生贡献,那么(g(m,j) = g(m,j-1))

    2. (P_j^2le m),这时候最小质因子是(P_j)的合数是存在的,所以(g(m,j))应该在(g(m,j-1))的基础上减掉最小质因子是(P_j)的合数(x)的贡献(F(x)),即:

      [g(m,j)=g(m,j-1)-X ]

      考虑如何计算(X),这里考虑容斥来做,首先把所有(P_j)的倍数的贡献去掉,但是其中有最小质因子小于(P_j)的贡献,这部分的贡献我们需要加回来先我们把质因子(P_j)提出来,由于(F(x))是完全积性函数,所以可以得到:

      [X=F(P_j)cdot(g(lfloor frac m{P_j} floor,j-1)-g(P_{j-1},j-1)) ]

      (g(lfloor frac m{P_j} floor,j-1))表示所有小于等于(lfloor frac m{P_j} floor)的质数和最小质因子大于等于(P_j)的合数的贡献,其中包含了的一些小于(P_j)质数的贡献,所以要去掉。而(g(P_{j-1},j-1))就表示小于(P_j)的质数带来的贡献

    那么我们就可以得到转移方程:

    [g(m,j)=egin{cases}g(m,j-1) & P_j^2>m \ g(m,j-1)-F(P_j)cdot(g(lfloor frac m{P_j} floor,j-1)-g(P_{j-1},j-1)) & P_j^2le mend{cases} ]

    考虑(P_j = m)的情况,这时候合数部分没有贡献,显然(g(m,j)=sum_{i=1}^jF(P_i))

    再考虑(j=0)的情况,这时候所有小于等于(m)的数都有贡献,那么(g(m,0)=sum_{i=1}^nF(i))

    更新一下:

    [g(m,j)=egin{cases}g(m,j-1) & P_j^2>m \ g(m,j-1)-F(P_j)cdot(g(lfloor frac m{P_j} floor,j-1)-sum_{i=1}^{j-1}F(P_i)) & P_j^2le mend{cases} ]

    所以最开始提到了计算(f(p))需要快速求值

    我们需要计算的所有素数的(f(p))的和可以这样表示:

    [sum_{p=2}^nf(p)=sum_{p=2}^nF(p)=g(n,mid Pmid) ]

    其中(mid Pmid)为集合(P)的大小,(pin P)

    观察上面的转移方程,可以发现(P_j^2>n)的素数是没有用的,所以需要用到的素数的数量是不超过(sqrt n)的,这部分素数我们可以线性筛筛出来

    对于给定的(n),由于(lfloor frac{lfloor frac ab floor }c floor=lfloor frac a{bcdot c} floor),观察递推式可知需要计算的(g(m,mid Pmid))(m)可以通过整除分块来预处理,不超过(sqrt n)

    由于需要计算的数字比较大,但是数量不多,考虑离散化一下用下标记录

    那么我们可以从最小的素数开始一步步处理计算出各个值,首先把每个值赋值为(g(w[i],0)),然后枚举每个素数,从最大的(w[i])开始递推,具体可以看代码理解一下

    比如计算(1-n)所有素数的和和所有素数的数量:

    // g1[i]表示 1~w[i]之间的所有素数和
    // g2[i]表示 1~w[i]之间所有素数的数量
    // pre[i]表示 前i个素数的和
    sqt = sqrt(n);
    m = 0;
    for(int l = 1, r; l <= n; l = r + 1){
        r = n / (n / l);
        w[++m] = n / l;
        if(w[m]<=sqt) id1[w[m]] = m;
        else id2[n/w[m]] = m;
        g1[m] = w[m] - 1;
        g2[m] = (2 + w[m]) * (w[m] - 1) / 2;
    }
    for(int p = 1; prime[p] * prime[p] <= w[1]; p++) for(int i = 1; i <= m and prime[p] * prime[p] <= w[i]; i++){
        int k = w[i] / prime[p] <= sqt ? id1[w[i]/prime[p]] : id2[n/(w[i]/prime[p])];
        g1[i] -= g1[k] - p + 1;
        g2[i] -= prime[p] * (g2[k] - pre[p-1]);
    }
    

    好了我们现在得到了所有素数对答案的贡献,现在定义一个新函数:

    [S(m,j)=sum_{i=2}^mf(i)cdot[operatorname{minp}(i)ge P_j] ]

    我们需要计算的前缀和就等于(S(n,1)+f(1))

    接下来我们考虑如何计算(S(m,j))

    枚举每个(i)的最小质因子和次数

    [egin{aligned}S(m,k)&=(sum_{kle i \ P_i^2le m}sum_{cge 1\P_i^cle m}f(P_i^c)cdot ([c>1]+S(lfloor frac{m}{P_i^c} floor,i+1))) + sum_{kle i\ P_ile m}f(P_i) \ &= (sum_{kle i \ P_i^2le m}sum_{cge 1\P_i^cle m}f(P_i^c)cdot ([c>1]+S(lfloor frac{m}{P_i^c} floor,i+1)) )+ g(m,mid Pmid) - sum_{i=1}^{k-1}f(P_i) \ &= (sum_{kle i \ P_i^2le m}sum_{cge 1\P_i^{c+1}le m}f(P_i^c)cdot S(lfloor frac{m}{P_i^c} floor,i+1)+f(P_i^{c+1})) + g(m,mid Pmid) - sum_{i=1}^{k-1}f(P_i) end{aligned} ]

    由于(f(P_i^c))是可以快速计算的,所以复杂度是有保障的,具体证明就不知道了阿巴阿巴阿巴

    比如计算(phi)(mu)的前缀和:

    // 计算欧拉函数前缀和
    LL SP(int m, int k){
        if(m<=1 or prime[k]>m) return 0;
        int id = m <= sqt ? id1[m] : id2[n / m];
        LL ret = g2[id] - pre[k-1] + k - 1;
        for(int i = k; prime[i] * prime[i] <= m; i++){
            LL p = prime[i], f = prime[i] - 1;
            for(int c = 1; p * prime[i] <= m; c++, p *= prime[i], f *= prime[i])
                ret += f * SP(m/p,i+1) + f * prime[i];
        }
        return ret;
    }
    // 计算莫比乌斯函数前缀和
    LL SM(int m, int k){
        if(m<=1 or prime[k]>m) return 0;
        int id = m <= sqt ? id1[m] : id2[n / m];
        LL ret = -g1[id] + k - 1;
        for(int i = k; prime[i] * prime[i] <= m; i++) ret -= SM(m/prime[i],i+1);
        return ret;
    }
    
    
    // 质数情况的莫比乌斯函数都是-1,所以前缀和就是质数数量取负
    // 而质数情况下的欧拉函数都是p-1,所以前缀和就是质数前缀和减去质数数量
    // 所以在计算之前做下面这步操作
    // for(int i = 1; i <= m; i++) g2[i] -= g1[i];
    
  • 相关阅读:
    java DMO及增删改查代码的自动生成
    如果有一天苹果免费了,支付宝怎么办
    jquery实现页面交互的几个小例子
    android中的所谓观察者模式
    随笔——runnable勘误以及其他
    android 源码角度全方位理解filter
    android 你的onfocuschangelistener和android:state_hovered为何不起作用
    android 如何阻断seekbar的触摸事件
    java 传值和传引用
    Android内存优化(使用SparseArray和ArrayMap代替HashMap)
  • 原文地址:https://www.cnblogs.com/kikokiko/p/13461322.html
Copyright © 2011-2022 走看看