初等数论-Base-1(筛法求素数,欧拉函数,欧几里得算法)
前言
初等数论在OI中应用的基础部分,同机房的AuSquare和zhou2003君早就写完了,一直划水偷懒的Hk-pls表示很方,这才开始了这篇博客.
\(P.S.\)可能会分部分发表。
Base-1
筛法求素数
埃式筛素数
问题:求\([1,n]\)中的所有素数
总体思路就是在\([2,n]\)中每当我们找到一个新的素数,在把它加入我们的素数队列的同时我们把它的倍数全部打上标记(包括它自己),下一个没有被标记的数就是新的素数。
void find_prime(int n){
    memset(used,0,sizeof(used));cnt=0;
    for (int i=2;i<=n;++i){
        if (!used[i]) {prm[++cnt]=i;
            for (int j=1;(i*j)<=n;++j) used[i*j]=1;}
    }
}
这种方法简单粗暴,非常好写,且可以适用于很多种情况,但是,因为一个合数可能包含多个质因子这个算法就会不可避免地产生重复标记的情况,产生时间上的弊端。于是——
欧拉筛法
欧拉筛法是一种经典的线性筛法,顾名思义,欧拉筛法的时间复杂度为\(O(n)\)
同样地,欧拉筛法也是根据一个核心原理:每个合数必定可以被它的最小的质因子筛掉。
void euler(int N){
    memset(used,0,sizeof(used));cnt=0;
    for (int i=2;i<=n;++i){
        if (!used[i]) prm[++cnt]=i;
        for (int j=1;j<=cnt;++j) 
        {used[i*prm[j]]=prm[j];if (!(i%prm[j])) break;}
        /*这句话是重点:如果找到了i的最小质因子prm[j]我们就立马退出,
        因为剩下的没有标记的数将会在其他数的prm[j]倍中出现而被标记,
        例如:i=6在prm[j]=2之后退出,而它剩下未标记的18则会在i=9时被标记*/
    }
}
于是由于及时地跳出,欧拉筛法的时间优势得以产生。
例题:[USACO08DEC]拍头Patting Heads
欧拉函数
定义\([1,n]\)中与\(n\)互素的数的个数为\(\phi(n),\)设\(n\)的素数表达式为\(n=p_1^{a_1}\times p_2^{a_2}\times \cdots\times p_n^{a_n}\)则:
证明:(来自李煜东的《算法竞赛进阶指南》)
利用容斥原理。
令\(p,q\)为\(n\)的不同的质因子,在\([1,n]\)中,能整除\(p\)的有\(\frac{n}{p}\)个能整除\(q\)的有\(\frac{n}{q}\)个,但是可能会有\(p,q\)的倍数被重复减去的情况,所以我们需要在后面补上一个\(\frac{n}{pq}\),则有:
同理可得欧拉函数。
性质
- 
若\(n\)为素数,则有:\(\varphi (n)=n-1\) 
- 
欧拉函数是积性函数 
- 
如果有\(p|n\),且\(p^2|n,\)则\(\varphi (n)=\varphi (n/p)\cdot p\)。 
 因为\(n\)和\(n/p\)包含相同的质因子,只是\(n\)的因子中的\(p\)的指数比\(n/p\)多\(1\),由前面的欧拉函数的公式可知,指数不影响\(\varphi (n)\),这种情况下只有\(n\)会影响\(\varphi (n)\),所以在这里,\(\varphi (n)=\varphi(n/p)\cdot p\)成立. 证毕。
求法
求\(\varphi(n)\)
本来我还以为这个就是个理论知识补充不会用来写的,然后上午写的博客,下午打的脸
int eular(int n){
    int apx=2,ans=n;
    while (apx*apx<=n){
        if (n%apx==0) ans=ans*(apx-1)/apx;
        while (n%apx==0) n/=apx;apx++;}
    if (n>1) ans=ans*(n-1)/n;
    return ans;
}
求所有\(\varphi(i),i\in[1,n]\)
void eular(int n){
    for (int i=2;i<=n;++i){
        if (!used[i]) {prm[++cnt]=i;phi[i]=i-1;}
        for (int j=1;j<=cnt;++j){
            if (prm[j]*i>n) break;
            used[prm[j]*i]=1;
            if (!(i%prm[j])) {phi[i*prm[j]]=phi[i]*prm[j];break;}
            //有没有发现这个写法和欧拉筛法十分的像?且这里要用上性质三
            else phi[i*prm[j]]=phi[i]*(prm[j]-1);
            //这里利用了欧拉函数是积性函数的性质
        }
    }
}
欧几里得辗转相除法(Euclid's algorithm)
我相信这个式子应该是非常有名的
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号