• min_25筛学习笔记【待填坑】


    看见ntf和pb两位大佬都来学了,然后就不自觉的来学了。


    我们考虑这样一个问题。

    $$ans=sum_{i=1}^nf(i)$$其中$1leq nleq 10^{10}$

    其中$f(i)$是一个非常奇怪的函数,并不像$mu(i),varphi(i),ivarphi(i)$那样具有那么好的性质。但是满足以下条件:

    1.若$p$为质数,则$f(p)$是一个关于$p$的多项式,比如$mu(p)=-1,varphi(p)=p-1$.

    2.若$p$为质数,$e$为正整数,则$f(p^e)$可以很快求出。(通常是$O(1)$)

    3.$f(n)$为积性函数。

    然后就可以使用min_25筛了。(顾名思义是min_25发明的)


    首先,我们需要知道min_25筛是埃氏筛的一个拓展,它的思想很大一部分借助于埃氏筛。

    回想一下埃氏筛,我们是每次将最小质因子为$p_i$的合数筛去,剩下的就是质数。

    我们知道这些最小质因子至多为$sqrt{n}$,所以合数可以通过枚举最小质因子来计算,质数我们则使用另外的方法。


    首先我们看质数的怎么做。

    $$sum_{i=1}^n[iin Prime]f(i)$$

    根据条件1,我们知道$f(i)$是一个多项式,这样的话我们可以按照次数将$f(i)$拆成$i^k$之和,因为$i^k$是一个完全积性函数(很快就有用的)。

    $$sum_{i=1}^n[iin Prime]i^k$$

    为了计算这个,我们需要引入一个辅助数组$g(n,j)$。(鬼知道是怎么想到的)

    $$g(n,j)=sum_{i=1}^n[iin Prime or minp(i)>P_j]i^k$$

    其中$minp(i)$表示$i$的最小质因子,所以

    $$sum_{i=1}^n[iin Prime]i^k=g(n,|P|)$$

    既然我们要使用质数,所以我们可以先用欧拉筛把所有$leq sqrt{n}$的质数筛出来,同时还要预处理$sum_{i=1}^j[iin Prime]i^k$.

    我们考虑dp计算。既然是埃氏筛,我们就要在$g(n,j-1)$中最小质因子为$P_j$的合数筛去。]'

    对于$P_jgeq sqrt{n}$的就不用管它了,因为肯定不可能。

    首先,由于它是完全积性函数,所以$P_j$可以直接提出来,剩下的减去$ileq lfloorfrac{n}{P_j} floor$中的数就可以了。

    这些数中要求质因子$geq P_j$,所以是$g(lfloorfrac{n}{P_j} floor,j-1)$,但是这里面质数被重复计算了,所以要减去里面的质数。

    $$g(n,j)=g(n,j-1)-P_j^k(g(lfloorfrac{n}{P_j} floor,j-1)-sum_{i=1}^{j-1}[iin Prime]i^k)$$

    但是这样的话是$O(n*|P|)$的,时间和空间都承受不了。但是我们发现我们可以使用一个优化。

    我们发现$g(n,j)$中$n$只有$O(sqrt{n})$种取值,因为每次递归的时候是$n$变为$lfloorfrac{n}{P_j} floor$,而我们发现

    $$lfloorfrac{lfloorfrac{a}{b} floor}{c} floor=lfloorfrac{a}{bc} floor$$

    所以$n$只会变为$lfloorfrac{n}{x} floor$,于是我们就直接“手动”离散化,这个可以看代码。

    然后$g(n,j)$的第二维也可以滚动数组滚掉。所以时间$O(sqrt{n}*|P|)$,空间$O(sqrt{n})$.


    预处理部分终于结束了,接下来我们考虑计算答案,首先我们还是需要一个辅助数组。

    $$S(n,j)=sum_{i=1}^n[minp(i)>P_j]f(i)$$

    像上面说的一样,分质数和合数两类计算。

    $$S(x,y)=g(x,|P|)-sum_{i=1}^{y}f(P_i)+sum_{P_kleq sqrt{x},k>j}sum_{e>0,P_k^eleq x}f(p_k^e)(S(lfloorfrac{x}{P_k^e} floor,k)+[e>1])$$

    前面两项指的是质数,后面的和式是枚举最小质因子$P_k$和它的次数$e$,

    这个跟$g$不同,$S$是要按照第二维倒着计算的,但是我们也可以使用递归的方法来计算。

    $S(n,0)+f(1)$就是最终答案。


    至于上面说的那个手动离散化,我们要开两个数组$id1$和$id2$,分别记录$leq sqrt{n}$和$>sqrt{n}$的部分的数值的编号。这样就不用map了,可以省掉一个log。

    至于时间复杂度?我也不知道,总之跟杜教筛差不多,甚至有时候比杜教筛还要快。

     1 #include<bits/stdc++.h>
     2 #define Rint register LL
     3 using namespace std;
     4 typedef long long LL;
     5 const int N = 1000003, mod = 1e9 + 7, inv2 = 500000004, inv3 = 333333336;
     6 LL n, Sqr, pri[N], tot, pre1[N], pre2[N], ind1[N], ind2[N], g1[N], g2[N], w[N], cnt;
     7 bool notp[N];
     8 inline void init(LL m){
     9     notp[0] = notp[1] = true;
    10     for(Rint i = 2;i <= m;i ++){
    11         if(!notp[i]){
    12             pri[++ tot] = i;
    13             pre1[tot] = (pre1[tot - 1] + i) % mod;
    14             pre2[tot] = (pre2[tot - 1] + i * i) % mod;
    15         }
    16         for(Rint j = 1;j <= tot && i * pri[j] <= m;j ++){
    17             notp[i * pri[j]] = true;
    18             if(!(i % pri[j])) break;
    19         }
    20     }
    21 }
    22 inline LL S(LL x, int y){
    23     if(pri[y] >= x) return 0;
    24     LL k = (x <= Sqr) ? ind1[x] : ind2[n / x];
    25     LL ans = (g2[k] - g1[k] + pre1[y] - pre2[y] + mod + mod) % mod;
    26     for(Rint i = y + 1;i <= tot && pri[i] * pri[i] <= x;i ++){
    27         LL pe = pri[i];
    28         for(Rint e = 1;pe <= x;e ++, pe *= pri[i]){
    29             LL xx = pe % mod;
    30             ans = (ans + xx * (xx - 1) % mod * (S(x / pe, i) + (e > 1))) % mod;
    31         }
    32     }
    33     return ans % mod;
    34 }
    35 int main(){
    36     scanf("%lld", &n);
    37     Sqr = sqrt(n);
    38     init(Sqr);
    39     for(Rint i = 1, last;i <= n;i = last + 1){
    40         last = n / (n / i);
    41         w[++ cnt] = n / i;
    42         LL xx = w[cnt] % mod;
    43         g1[cnt] = (xx * (xx + 1) / 2 + mod - 1) % mod;
    44         g2[cnt] = (xx * (xx + 1) / 2 % mod * (2 * xx + 1) % mod * inv3 % mod + mod - 1) % mod;
    45         if(n / i <= Sqr) ind1[w[cnt]] = cnt;
    46         else ind2[last] = cnt;
    47     }
    48     for(Rint i = 1;i <= tot;i ++)
    49         for(Rint j = 1;j <= cnt && pri[i] * pri[i] <= w[j];j ++){
    50             LL k = (w[j] / pri[i] <= Sqr) ? ind1[w[j] / pri[i]] : ind2[n / (w[j] / pri[i])];
    51             g1[j] -= pri[i] * (g1[k] - pre1[i - 1] + mod) % mod;
    52             g2[j] -= pri[i] * pri[i] % mod * (g2[k] - pre2[i - 1] + mod) % mod;
    53             if(g1[j] < 0) g1[j] += mod;
    54             if(g2[j] < 0) g2[j] += mod;
    55         }
    56     printf("%lld", (S(n, 0) + 1) % mod);
    57 }
    luogu5325

     练习题:

    UOJ188

  • 相关阅读:
    如何学好编程
    进制转换
    第五周学习总结 20201204 于瀛鹏
    xor运算
    20201204 于瀛鹏 第四周学习总结
    20201204 于瀛鹏 第三周学习总结
    IEEE754浮点数
    base64编码
    罗马数字(1-3999)转阿拉伯数字
    俄罗斯方块
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/10827172.html
Copyright © 2020-2023  润新知