zoukankan      html  css  js  c++  java
  • 04 线性质数筛 欧拉函数 莫比乌斯函数 狄利克雷卷积(待补)

    线性素数筛

    https://www.luogu.com.cn/problem/P3383

    板子

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll maxn=1e8+1000;
    int t,p[maxn],k,q;
    bool vis[maxn];
    void make_prime(ll n){
        fill(vis,vis+n,0);
        ll cnt=0;
        for(int i=2;i<=n;i++){
            if(!vis[i]) p[++cnt]=i;
            for(int j=1;p[j]*i<=n&&j<=cnt;j++){
                vis[p[j]*i]=1;
                if(i%p[j]==0) break;
            }
        }
    }
    int main(){
        //freopen("in.txt","r",stdin);
        ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
        cin>>t>>q;
        make_prime(t);
        while(q--){
            cin>>k;
            cout<<p[k]<<endl;
        }
        return 0;
    }
    

    欧拉函数

    定义:

     $\varphi \left( x \right) $即为小于$x$的所有与$x$互质的整数的个数。

    朴素单个欧拉函数值求法:

    ll euler(ll n){
        ll res=n;
        for(ll i=2;i*i<=n;i++){
            if(n%i==0){
                res-=res/i;
                while(n%i==0)
                    n/=i;
            }
        }
        if(n>1) res-=res/n;
        return res;
    }

    每次遇到可以整除$n$的数就从答案中减去所有该数的倍数,并对$n$进行调整。考虑到当$n$为质数时,整体的复杂度为$O\left( \sqrt{n} \right) $。

    相关定理:

     1.当$p$为质数时,$\varphi \left( p \right) =p-1$,显然。

     2.若$p$为质数,且$n=p^k$,则$\varphi \left( n \right) =p^k-p^{k-1}$。

     3.欧拉函数为积性函数(我还不知道积性函数是什么),特殊的当$n$为奇数时,$\varphi \left( 2n \right) =\varphi \left( n \right) $。

     4.当$n>2$时,$\varphi(n)$必为偶数。

     5.比$n$小的所有的数的和为$n\cdot \varphi \left( n \right) /2$。

     6.若$a$与$n$互质,则有$a^{\varphi \left( n \right)}\equiv 1\left( \text{mod} n \right) \,\,$,$a^{b\,\,\left( \text{mod} \varphi \left( n \right) \right)}\equiv 1\left( \text{mod} n \right) \,\,$。

    例题:

     P5091 【模板】扩展欧拉定理

     其他没啥就是这个$b$在读入的时候要按照字符读入然后取模,复杂度为$O\left( \sqrt{m}+\log  b \right) $,大概就是$1e7$的样子。

     TM wa了老半天才知道定理是有使用条件的,最终的定理长这样:

    $$a^b=\left\{ \begin{array}{c}
    a^b\,\, ,b<\varphi \left( m \right)\\
    a^{b\,\,\text{mod} \varphi \left( m \right) +\varphi \left( m \right)}\,\,,b\ge \varphi \left( m \right)\\
    \end{array} \right. $$

    且此时有$gcd\left( a,m \right) \ne 1$

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll maxn=2e5+1000;
    ll a,b,mod;
    ll euler(ll n){
        ll res=n;
        for(ll i=2;i*i<=n;i++){
            if(n%i==0){
                res-=res/i;
                while(n%i==0)
                    n/=i;
            }
        }
        if(n>1) res-=res/n;
        return res;
    }
    inline ll read(ll mod){
        ll x=0;char c=getchar();
        int yes=0;
        while (c<'0'||c>'9') c=getchar();
        while (c>='0'&&c<='9'){
            x=x*10+c-'0';
            if(x>=mod) x=x%mod,yes=1;
            c=getchar();
        }
        if(yes) return x;
        else return -x;
    }
    ll qpow(ll a,ll k){
        ll res=1,x=a;
        while(k){
            if(k&1){
                res=x*res%mod;   
            }
            x=x*x%mod;
            k>>=1;
        }
        return res;
    }
    int main(){
       // freopen("in.txt","r",stdin);
        ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
        a=-read(1e9+100),mod=-read(1e9+100);
        ll phi=euler(mod);
        b=read(phi);
        if(a%mod==0) cout<<0<<endl;
        else{
            if(b>=0) 
                cout<<qpow(a,b+phi)%mod<<endl;
            else 
                cout<<qpow(a,-b)%mod<<endl;
        }
        return 0;
    }

     P2158 [SDOI2008]仪仗队

    实际上就是求出$1\sim n$内所有互质的点的个数$num$,然后答案就等于$ans=num+2-1$,加$2$是因为$(0,1),(1,0)$未算入,减一是因为$(1,1)$重复计算了。

    所以有$ans=2 \sum_{i=0}^{n-1}{\varphi \left( i \right)}+1$。那么就需要快速的求出$\varphi \left( 1 \right) \sim \varphi \left( n \right) $,显然按照之前的求法是不好的,所以这里要用到线性筛法求出,复杂度为$O(n)$废话不然为什么叫线性筛。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll maxn=2e5+1000;
    ll vis[maxn],p[maxn],phi[maxn];
    ll euler(ll n){//朴素单个欧拉函数 O(sqrt(n)) 
        ll res=n;
        for(ll i=2;i*i<=n;i++){
            if(n%i==0){
                res-=res/i;
                while(n%i==0)
                    n/=i;
            }
        }
        if(n>1) res-=res/n;
        return res;
    }
    void neuler(ll n){//得到1~n-1所有的欧拉函数 O(n)
        ll cnt=0;
        phi[1]=1;
        for(int i=2;i<n;i++){
            if(!vis[i]){
                p[cnt++]=i;
                phi[i]=i-1;
            }
            for(int j=0;j<cnt&&i*p[j]<n;j++){
                vis[i*p[j]]=1;
                if(i%p[j]==0){
                    phi[i*p[j]]=phi[i]*p[j];
                    break;
                }
                phi[i*p[j]]=phi[i]*(p[j]-1);
            }
        }
    }
    int main(){
        //freopen("in.txt","r",stdin);
        ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
        ll  n;
        cin>>n;
        neuler(n+1);
        ll res=0;
        if(n==1){
            cout<<0<<endl;
            return 0;
        }
        for(int i=0;i<n;i++) res+=phi[i]; 
        cout<<2*res+1<<endl;
        return 0;
    }

    可以看到,其实基本的代码结构是和原本的质数筛相同的。基本上有三点不同,比如第六行的对$\varphi (i)$的赋值和第十一行和十四行的递推。从此也就可以看出线性筛的适用范围。就是当$n$和$m$互质时有:$f\left( n\cdot m \right) =f\left( n \right) \cdot f\left( m \right) $,这也就是积性函数的定义。这个东西太高级了,现在就先当黑箱子用吧,待补(咕咕)。

    莫比乌斯函数

    定义

    $$\mu \left( x \right) =\begin{cases}
    \,\, 1 \text{,}x=1\\
    -1^k\text{,}x\text{无平方因数且}x=p_1p_2p_3...p_k\\
    \,\, 0 \text{,}x\text{有平方因数}\\
    \end{cases}$$换句人话就是说如果可以表示为奇数个不同素数的乘积就是-1,如果可以表示为偶数个不同素数的乘积就是1,如果表示出来有相同素数就是0。

    单个莫比乌斯函数的朴素算法

     1 int mob(ll n){//单个函数的算法 O(sqrt(n))
     2     ll cnt=0,temp=0;
     3     for(ll i=2;i*i<=n;i++){
     4         temp=0;
     5         if(n%i==0){
     6             while(n%i==0) temp++,n/=i;
     7             if(temp>1) return 0;
     8             cnt++;
     9         }
    10     }
    11     if(n!=1) cnt++;
    12     return (cnt&1)?-1:1;
    13 }

    相关定理

    1.

    $$\sum_{d\mid n}{\mu \left( d \right)}=\left\{ \begin{array}{c}
    1 \left( n=1 \right)\\
    0 \left( n\ne 1 \right)\\
    \end{array} \right. $$

    当$n=1$时是显然的;当$n \ne 1$时,$d$相当于从$n$的素数乘积表示中选取,故有:

    $$\sum_{d\mid n}{\mu \left( d \right)}=\sum_{i=0}^k{\left( -1 \right) ^iC_{k}^{i}}$$

    由组合的性质可知后者等于$0$。嗷看了半天才知道,这个定理可以将$n=1$的条件判断变成左边的式子,这样在某些时候就可以化简表达式。

    2.

    $$\sum_{d\mid n}^{}{\cfrac{\mu \left( d \right)}{d}}=\cfrac{\varphi \left( n \right)}{n}$$

    证明略,要用到一些我不会的东西

    线性筛法

    显然当$n,m$互质时,由于二者没有共同的因数,所以乘积的表示中不会有同项,由定义可知此时有:

    $$\mu \left( n\cdot m \right) =\mu \left( n \right) \cdot \mu \left( m \right) $$

    故莫比乌斯函数为积性函数,可以用线性筛法求出。

     1 int u[maxn],p[maxn];
     2 bool vis[maxn];
     3 void nmob(ll n){
     4     u[1]=1;
     5     ll cnt=0;
     6     for(int i=2;i<=n;i++){
     7         if(!vis[i])p[++cnt]=i,u[i]=-1;
     8         for(int j=1;j<=cnt&&i*p[j]<=n;j++){
     9             vis[i*p[j]]=1;
    10             if(i%p[j]) u[i*p[j]]=-u[i];
    11             else break;
    12         }
    13     }
    14 }

    例题

    HDOJ-2841

    类似于上例中的仪仗队但是对$x,y$的限制不同了,有俩种方法。第一种是利用容斥思想将$y$分解然后对于每个$n$除去相应的因数的个数,但是有的数字会被重复删去,所以要将删去的再次加回来,而这个系数正是该数对应的莫比乌斯函数值;重点看一下第二种方法,利用上面说的等价判断有:

    $$\sum_{i=1}^n{\sum_{j=1}^n{\left[ gcd\left( i,j \right) ==1 \right]}}
    \\
    =\sum_{i=1}^n{\sum_{j=1}^n{\sum_{d\mid gcd\left( i,j \right)}^{}{\mu \left( d \right)}}}
    \\
    =\sum_{d=1}^{\min \left( n,m \right)}{\mu \left( d \right) \sum_{i=1}^{n/d}{1}\sum_{j=1}^{m/d}{1}}
    \\
    =\sum_{d=1}^{\min \left( n,m \right)}{\mu \left( d \right) \cdot \frac{n}{d}\cdot \frac{m}{d}}$$

    哇,说实话这个变换真的秀,这要我就不可能想得到啊。第一步用的就是定理,但是第二步就很巧妙。因为$d\mid gcd\left( i,j \right) $中要让$d$存在,那么$gcd\left( i,j \right) $必须要大于等$d$所以每一个$d$的贡献就是$$\mu \left( d \right) \sum_{i=1}^{n/d}{1}\sum_{j=1}^{m/d}{1}$$后面之所以是从$1$到$n/d$,就是看$n$最多可以被$d$的几倍整除,然后前面之所以是$\min \left( n,m \right) $是因为$d$最大也只能到这个值且此时$n,m$有倍数关系。然后这个问题就变成了线性问题,结合线性筛就直接就是裸题了,相比于第一种方法代码量要小很多。果然数学越好代码越少。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const ll maxn=2e5+1000;
     5 int mob(ll n){//单个函数的算法 O(sqrt(n))
     6     ll cnt=0,temp=0;
     7     for(ll i=2;i*i<=n;i++){
     8         temp=0;
     9         if(n%i==0){
    10             while(n%i==0) temp++,n/=i;
    11             if(temp>1) return 0;
    12             cnt++;
    13         }
    14     }
    15     if(n!=1) cnt++;
    16     return (cnt&1)?-1:1;
    17 }
    18 int u[maxn],p[maxn];
    19 bool vis[maxn];
    20 void nmob(ll n){
    21     u[1]=1;
    22     ll cnt=0;
    23     for(int i=2;i<=n;i++){
    24         if(!vis[i])p[++cnt]=i,u[i]=-1;
    25         for(int j=1;j<=cnt&&i*p[j]<=n;j++){
    26             vis[i*p[j]]=1;
    27             if(i%p[j]) u[i*p[j]]=-u[i];
    28             else break;
    29         }
    30     }
    31 }
    32 ll t,n,m;
    33 int main(){
    34     //freopen("in.txt","r",stdin);
    35     ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    36     cin>>t;
    37     nmob(1e5+100);
    38     while(t--){
    39         cin>>n>>m;
    40         ll xiao=min(n,m),res=0;
    41         for(int d=1;d<=xiao;d++){
    42             ll a=n/d,b=m/d;
    43             res+=a*b*u[d];
    44         }
    45         cout<<res<<endl;
    46     }
    47     return 0;
    48 }

  • 相关阅读:
    个人博客作业-软件案例分析1 博客园和CSDN
    第一次结对项目
    个人项目作业(1)
    个人博客作业(1)
    第一次个人作业
    面向对象第四单元总结和期末总结
    面向对象第三单元总结
    面向对象第二单元总结
    UML——BUAA_OO 第四单元总结 & 课程总结
    JML——BUAA_OO 第三单元总结
  • 原文地址:https://www.cnblogs.com/Zabreture/p/13419558.html
Copyright © 2011-2022 走看看