zoukankan      html  css  js  c++  java
  • 简易数论函数变换学习

    积性函数

    当$(n,m) = 1$时有$f(nm) = f(n)f(m)$,则称$f(x)$ 为积性函数。

    线性筛法

    对于每一个数字$n$,用其最小的质因数筛去,考虑最小质因数 $p$ 与数字 $n$ 的三种情况

    1. $n = p$ 。

    2. $p|n, p<n$

    3. $else$

    三种情况分别考虑即可。

    以欧拉函数为例:

    1. $phi (i) = i-1$

    2. $phi (tp) = phi (t) imes p$

    3. $phi (tp) = (p-1) imes phi(t) $ 这样可以 $O(n)$ 筛出相应函数

    例:求出 1~n! 中与 m! 互质的数字个数。

    x 与 m! 互质 <-> x 与 1 ~ m 互质 <-> x 不含有 ≤ m的质数。

    这样考虑 $x = m!t + k$

    $p | x$ <=> $p | k$,这样有 $ans = m! prod {1 - frac{1}{p_i}}$

    应用阶乘法筛出逆元(用 $(P-1)!^{-1}$ 逆推),预处理出后一部分前缀积,复杂度 $O(n + T)$

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    #define LL long long
    #define N 10000010
    
    using namespace std;
    
    int tot, prime[N], inv[N], fac[N], P, prev[N];
    bool v[N];
    
    int mul(int a,int b)
    {
        return a*(LL)b % (LL)P;
    }
    
    int qpow(int x,int n)
    {
        int ans = 1;
        for(;n;n>>=1,x = mul(x, x))
            if(n&1) ans = mul(ans, x);
        return ans;
    }
    
    void init()
    {
        fac[0] = 1;
        int nl = min(N,P);
        for(int i=1;i < nl ;i++) fac[i] = mul(fac[i-1], i);
        int tmp = qpow(fac[nl-1], P-2);
        for(int i=nl-1;i>=1;i--)
        {
            inv[i] = mul(tmp, fac[i-1]);
            tmp  = mul(tmp, i);
        }
    }
    
    int main()
    {
        int T;
        cin>>T>>P;
        init();
        for(int i=2;i<N;i++)
        {
            if(!v[i]) prime[++tot] = i;
            for(int j=1;i*prime[j]<N;j++)
            {
                v[i*prime[j]] = 1;
                if(i%prime[j]==0) break;
            }
        }
        int j = 1;
        prev[0] = 1;
        for(int i=1;i<N;i++)
        {
            prev[i] = prev[i-1];
            if(j<=tot && prime[j]==i)
            {
                prev[i] += P - mul(prev[i] ,inv[i]);
                if(prev[i] >= P) prev[i] -= P;
                j++;
            }
        }
        int n,m;
        while(T--)
        {
            scanf("%d%d",&n,&m);
            printf("%d
    ",mul(fac[n], prev[m]));
        }
        return 0;
    }
    View Code

    常见积性数论函数

    $mu (i)$ :狄里克莱卷积里的逆元函数

    当i为若干个不同质数相乘得到,则 $mu (i) = (-1) ^ {cnt}$ ($cnt$ 表示i中质因数的个数)

    不然 $mu (i) = 0$

    1. $mu (i) = -1$

    2. $mu (tp) = 0$

    3. $mu (tp) = -mu (t)$

    $e (i)$ :判别函数, [i =1],相当于狄里克莱卷积之中的 '1'

    $d (i)$ :约数个数,建立辅助函数 $a (i)$ 表示最小质因数的指数

    $d(i) = prod {t_i + 1}, i = p_1^{t_1} p_2^{t_2} ... p_{cnt}^{t_{cnt}}$

    1. $d(i) = 2, a(i) = 1$

    2. $d(tp) =  frac{d(t)}{a(t)+1} imes (a(t)+2) , a(tp) = a(t) + 1$

    3. $d(tp) = 2 d(t)$

    $sigma (i)$ :约数和

    记 $F(p,t) = frac{1 - p ^ {t + 1}}{1 - p}$

    $sigma (i) = prod { F(p_i, t_i)  }$

    1. $sigma (i) = i+1$

    2. $sigma (tp) = frac{sigma (t)}{ F(p, a(t)) } imes F(p, a(t)+1)$

    3. $sigma (tp) = sigma (t) (p+1)$

     狄里克莱卷积

    首先有两个常用的性质:

    $sum _{d|n} {phi (d)} = n, phi imes I = id$

    $sum_{d|n} {mu (d)} = e(n), mu imes I = e$

    如果 $f, g$ 为积性函数,则有 $f imes g, f cdot g$ 为积性函数。

    小Trick:

    $sum{f( [frac{n}{i}] )}$ 可以根据 $[frac{n}{i}]$ 的值分成 $O(sqrt n)$ 块,并且有 $[frac{n}{i}]$ 相同的 i 连续。

     例题若干:

    1D GCD sum:

    $$sum_{i=1}^n {(n,i)}= sum_{i=1}^n { sum_{d|(n,i)}{phi(d)} } = sum_{d|n} { [frac{n}{d}] phi(d)}$$

    2D GCD sum:

    $$sum_{i=1}^n { sum_{j=1}^m {(i,j)} } = sum_{i=1}^n{ sum_{j=1}^m{ sum_{d|(i,j)} {phi (d)}  }   } = sum_{d=1}^n{ phi (d) [frac{n}{d}] [frac{m}{d}] }$$

    2D prime count:

    $$sum_{i=1}^n {sum_{j=1}^m {e((i,j))}} = sum_{i=1}^n{ sum_{j=1}^m{ sum_{d|(i,j)} {mu (d)}  }   } = sum_{d=1}^n{ mu (d) [frac{n}{d}] [frac{m}{d}] }$$

    1D prime sum:

    $$sum_{1 leq i leq n, (n,i) = 1}i = sum_{i=1}^n { i e((n,i))} = frac{n}{2}(mu imes id + mu imes I) = frac{n}{2}(phi + e) = frac{n}{2} (phi(n) + e(n))$$

    莫比乌斯反演

    由于在狄里克莱卷积中 $mu (x)$ 为 $I(x)$ 的逆函数,这样有,对于 $F = f imes I$, $f = F imes mu$,即为莫比乌斯反演。

     1 D LCM sum:

    $$Ans = sum_{i=1}^n { [n,i] } = n sum_{i=1}^n { frac{i}{(n,i)} } = n sum_{d|n}{ sum_{i leq frac{n}{d}}{i cdot e((frac{n}{d},i))} } = n sum_{d|n}{ g(frac{n}{d}) }$$

    而$g(n) = frac{n}{2}(phi + e)$

    代入得$$Ans = frac{n^2}{2} ( (phi imes I) + 1)$$

     2 D LCM sum:

    $$sum_{i=1}^n {sum_{j=1}^m {[i,j]}} = sum_{d=1}^n { d imes S([frac{n}{d}], [frac{m}{d}]) }$$

    $$S(n,m) = sum_{i=1}^m{ sum_{j=1}^m { ij cdot e((i,j))} } = sum_{d=1}^n{mu (d) d^2 S([frac{n}{d}])  S([frac{m}{d}]) }$$

    $$S(n) = frac{n(n+1)}{2}$$

    两个部分都根据$frac{n}{i}$ $O(sqrt n)$分段,时间复杂度 $O(n)$

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    #define LL long long
    #define P 20101009LL
    #define N 10000010
    
    using namespace std;
    
    int n,m,tot,prime[N/5],mu[N],Sum[N];
    bool v[N];
    
    int mul(int a,int b)
    {
        return (a * (LL)b) % P;
    }
    
    int F(int n,int m)
    {
        int j;
        LL ans = 0;
        for(int i=1;i<=n;i=j+1)
        {
            j = min(n/(n/i), m/(m/i));
            ans += (mu[j]-(LL)mu[i-1]+P)%P * (LL)Sum[n/i]%P * (LL)Sum[m/i]%P;
            if(ans>=P) ans -= P;
        }
        return ans;
    }
    
    int main()
    {
        freopen("nt2011_table.in","r",stdin);
        freopen("nt2011_table.out","w",stdout);
        int n,m;
        cin>>n>>m;
        if(n>m) swap(n,m);
        mu[1] = 1;
        Sum[1] = 1;
        for(int i=2;i<=m;i++)
        {
            Sum[i]=Sum[i-1]+i;
            if(Sum[i]>=P) Sum[i]-=P;
        }
        for(int i=2;i<=n;i++)
        {
            if(!v[i])
            {
                prime[++tot] = i;
                mu[i] = P-1;
            }
            for(int j=1;j<=tot && i*prime[j]<=n;j++)
            {
                v[i*prime[j]] = 1;
                mu[i*prime[j]] = P-mu[i];
                if(i%prime[j]==0)
                {
                    mu[i*prime[j]] = 0;
                    break;
                }
            }
        }
        for(int i=1;i<=n;i++) mu[i] = mu[i]*(LL)i%P * (LL)i%P;
        for(int i=1;i<=n;i++)
        {
            mu[i] += mu[i-1];
            if(mu[i]>=P) mu[i] -= P;
        }
        int j;
        LL ans = 0;
        for(int i=1;i<=n;i=j+1)
        {
            j = min(n/(n/i), m/(m/i));
            ans += (Sum[j]-Sum[i-1]+P)%P * F(n/i,m/i)%P;
            if(ans>=P) ans -= P; 
        }
        cout << ans << endl;
    }
    View Code

    记 $f(n) = frac{1}{n},$,$f = F imes I$

    这样 $g = n cdot F = mu cdot id$,这样有$g$为积性函数。

    $$Ans = sum_{i=1}^n {sum_{j=1}^m { ij sum_{d|(i,j)}{F(d)} }} = sum_{d=1}^n{ g(d) d cdot S([frac{n}{d}]) S([frac{m}{d}]) }$$

    $g(p) = 1-p$

    $g(ip) = g(i)$,$(i,p) = p$,线性筛即可。

  • 相关阅读:
    Tomcat安装配置
    重新捡起手中的笔
    如何拥有一套自己的信用卡分销系统
    关于信用卡分销系统的简单介绍
    jQuery 基础教程(第3版) ---第十章习题答案
    jQuery 基础教程(第3版) ---第九章习题答案
    jQuery 基础教程(第3版) ---第八章习题答案
    jQuery 基础教程(第3版) ---第七章习题答案
    jQuery 基础教程(第3版) ---第六章习题答案
    jQuery 基础教程(第3版) ---第五章习题答案
  • 原文地址:https://www.cnblogs.com/lawyer/p/7217110.html
Copyright © 2011-2022 走看看