• Luogu 4213 【模板】杜教筛(Sum)


    当作杜教筛的笔记吧。

    杜教筛

    要求一个积性函数$f(i)$的前缀和,现在这个东西并不是很好算,那么我们考虑让它卷上另外一个积性函数$g(i)$,使$(f * g)$的前缀和变得方便计算,然后再反推出这个$f$函数的前缀和。

    $$sum_{i = 1}^{n}(f * g)(i) = sum_{i = 1}^{n}sum_{d | i}g(d)f(frac{i}{d}) = sum_{d = 1}^{n}g(d)sum_{i = 1}^{left lfloor frac{n}{d} ight floor}f(i) = sum_{d = 1}^{n}g(d)S(frac{n}{d}{})$$

    把$g(1)S(n)$移过来

    $$g(1)S(n) = sum_{i = 1}^{n}(f * g)(i) - sum_{i = 2}^{n}g(i)S(left lfloor frac{n}{i} ight floor)$$

    这个式子就是杜教筛的精髓了。

    我们可以先筛出$[1, sqrt{n}]$区间内的该积性函数的前缀和,然后再分块递归求解$(sqrt{n}, n]$中的该函数的前缀和,可以做到$O(n^{frac{2}{3}})$的优秀的复杂度(并不会这个复杂度的证明)。

    应该用一个哈希表存一下已经计算过的各个$S(n)$的值($unordered\_map$)。

    接下来的问题就是考虑如何搭配出这个积性函数$g$了。

    模板题

    考虑如何计算$mu$和$varphi$。

    我们知道$mu * I = epsilon$,那么有

    $$S(n) = sum_{i = 1}^{n}epsilon(i) - sum_{i = 2}^{n}S(left lfloor frac{n}{i} ight floor)$$

    滑稽吧,$epsilon$的前缀和还不是$1$。

    我们又知道$varphi * I = id$,那么又有

    $$S(n) = sum_{i = 1}^{n}id(i) - sum_{i = 2}^{n}S(left lfloor frac{n}{i} ight floor)$$

    而$sum_{i = 1}^{n}id(i) = sum_{i = 1}^{n}i = frac{i(i + 1)}{2}$。

    解决了!

    Code:

    #include <cstdio>
    #include <cstring>
    #include <unordered_map>
    using namespace std;
    typedef long long ll;
    
    const int N = 5e6 + 5;
    const int Maxn = 5e6;
    
    int testCase, pCnt = 0, pri[N], mu[N], phi[N];
    ll sumMu[N], sumPhi[N];
    bool np[N];
    unordered_map <int, ll> sMu, sPhi;
    
    template <typename T>
    inline void read(T &X) {
        X = 0; char ch = 0; T op = 1;
        for (; ch > '9'|| ch < '0'; ch = getchar())
            if (ch == '-') op = -1;
        for (; ch >= '0' && ch <= '9'; ch = getchar())
            X = (X << 3) + (X << 1) + ch - 48;
        X *= op;
    }
    
    inline void sieve() {
        mu[1] = 1, phi[1] = 1;
        for (int i = 2; i <= Maxn; i++) {
            if (!np[i]) pri[++pCnt] = i, phi[i] = i - 1, mu[i] = -1;
            for (int j = 1; j <= pCnt && i * pri[j] <= Maxn; j++) {
                np[i * pri[j]] = 1;
                if (i % pri[j] == 0) {
                    phi[i * pri[j]] = phi[i] * pri[j];
                    mu[i * pri[j]] = 0;
                    break;
                }
                phi[i * pri[j]] = phi[i] * phi[pri[j]];
                mu[i * pri[j]] = -mu[i];
            }
        }
        
        for (int i = 1; i <= Maxn; i++) {
            sumMu[i] = sumMu[i - 1] + mu[i];
            sumPhi[i] = sumPhi[i - 1] + phi[i];
         }
    }
    
    ll getPhi(int n) {
        if (n <= Maxn) return sumPhi[n];
        if (sPhi[n]) return sPhi[n];
        ll res = 1LL * n * (n + 1) / 2;
        for (int l = 2, r; l <= n; l = r + 1) {
            r = (n / (n / l));
            res -= 1LL * (r - l + 1) * getPhi(n / l);
        }
        return sPhi[n] = res;
    }
    
    ll getMu(int n) {
        if (n <= Maxn) return sumMu[n];
        if (sMu[n]) return sMu[n];
        ll res = 1LL;
        for (int l = 2, r; l <= n; l = r + 1) {
            r = (n / (n / l));
            res -= 1LL * (r - l + 1) * getMu(n / l);
        }
        return sMu[n] = res;
    }
    
    int main() {
        sieve();
        read(testCase);
        for (int n; testCase--; ) {
            read(n);
            printf("%lld %lld
    ", getPhi(n), getMu(n));
        }
        return 0;
    }
    View Code

    感觉时限特别急,能别开$long long$就别开。

  • 相关阅读:
    Maven的安装及更改下载仓库
    maven如何配置
    hibernate+mysql的连接池配置
    Hibernate配置方式
    如何获得Webapp的根项目路径 即ServletContext.getRealPath() 的输入参数要以"/"开头
    JAVA WEB项目中各种路径的获取
    java创建文件和目录
    Button或者ImageButton的背景设为透明或者半透明
    ScrollView中嵌套ListView的问题
    Android中RelativeLayout各个属性的含义
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/10211296.html
Copyright © 2020-2023  润新知