zoukankan      html  css  js  c++  java
  • 欧拉函数

    part 1.什么是欧拉函数?

    在数论,对正整数 (n) ,欧拉函数是小于或等于 (n) 的正整数中与 (n) 互质的数的数目(因此 (varphi(1)=1))。此函数以其首名研究者欧拉命名 (Euler) ,它又称为 Euler's totient function、(varphi) 函数、欧拉商数等。——百度百科


    part 2.一些性质

    1. (n) 为质数,则 (varphi(n)=n-1)

    2. (forall n>1)([1,n]) 中与 (n) 互质的数的和为 (dfrac{n imesvarphi(n)}{2})

    3. (a,b) 互质,则 (varphi(a imes b)=varphi(a) imesvarphi(b))

    4. (a,m) 互质,则 (a^{varphi(m)}equiv 1pmod m)

    5. (sum_{dmid n}varphi(d)=n)

    6. (nmod p=0),则 (varphi(n imes p)=varphi(n) imes p)。 若 (nmod p ot= 0),则 (varphi(n imes p)=varphi(n) imes (p-1))


    part 3.怎么求?

    1.通式

    假设正整数 (n)(m) 个质因数,分别为 (p_1,p_2,p_3,cdots,p_m),则 (varphi(n)) 有以下通式:

    [varphi(n)=n imes(1-frac{1}{p_1}) imes(1-frac{1}{p_2}) imes(1-frac{1}{p_3}) imescdots imes(1-frac{1}{p_m}) ]

    即:

    [varphi(n)=n imesprod_{i=1}^{m} (1-frac{1}{p_i}) ]

    推导过程的话,懒得打了...放个链接吧。

    根据通式我们可以写出以下代码:

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

    2.筛法

    百度百科上的筛法版本:

    /*
    特性 :
    1.若a为质数,phi[a]=a-1;
    2.若a为质数,b mod a=0,phi[a*b]=phi[b]*a
    3.若a,b互质,phi[a*b]=phi[a]*phi[b](当a为质数时,if b mod a!=0 ,phi[a*b]=phi[a]*phi[b])
    */
    int m[n],phi[n],p[n],nump;
    //m[i]标记i是否为素数,0为素数,1不为素数;p是存放素数的数组;nump是当前素数个数;phi[i]为欧拉函数
    int main()
    {
        phi[1]=1;
        for (int i=2;i<=n;i++)
        {
            if (!m[i])//i为素数
            {
                p[++nump]=i;//将i加入素数数组p中
                phi[i]=i-1;//因为i是素数,由特性得知    
            }    
            for (int j=1;j<=nump&&p[j]*i<=n;j++)  //用当前已得到的素数数组p筛,筛去p[j]*i
            {
                m[p[j]*i]=1;//可以确定i*p[j]不是素数 
                if (i%p[j]==0) //看p[j]是否是i的约数,因为素数p[j],等于判断i和p[j]是否互质 
                {
                    phi[p[j]*i]=phi[i]*p[j]; //特性2
                    break;
                }
                else phi[p[j]*i]=phi[i]*(p[j]-1); //互质,特性3其,p[j]-1就是phi[p[j]]   
            }
        }
    }
    

    part 4.例题

    (1)SPOJ4141 Euler Totient Function

    SPOJ题目链接

    洛谷题目链接

    模板题,不做分析。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int euler(int n) 
    {
        int ans=n;
        for(int i=2;i*i<=n;i++) 
        {
            if(n%i==0)
            {
                ans=ans/i*(i-1);
                while(n%i==0) n/=i;
            }
        }
        if(n>1) ans=ans/n*(n-1);
        return ans;
    }
    int main()
    {
        int T;
        cin>>T;
        while(T--)
        {
            int n;
            cin>>n;
            cout<<euler(n)<<endl;
        }
        return 0;
    }
    

    (2)HDU2588 GCD

    题目链接

    (p=gcd(x,n),;n=p imes a,;x=p imes b),显然 (a)(b) 一定互质。

    所以我们可以枚举公约数 (p),通过 (ndiv p) 求出 (a),然后求区间 ([1,a]) 内有多少个与 (a) 互质的 (b) 即可。

    我们可以用上面的欧拉函数 (varphi(a)) 来求与 (a) 互质的数。

    code:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
    ll euler(ll n) 
    {
        ll ans=n;
        for(int i=2;i*i<=n;i++) 
        {
            if(n%i==0)
            {
                ans=ans/i*(i-1);
                while(n%i==0) n/=i;
            }
        }
        if(n>1) ans=ans/n*(n-1);
        return ans;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
        	int n,m;
            scanf("%d%d",&n,&m);
            ll ans=0;
            for(int i=1;i*i<=n;i++)
            {
                if(n%i==0) //i或者n/i为公约数的情况
                {
                    if(i>=m) ans+=euler(n/i); //如果i为公约数
                    if(i*i!=n && n/i>=m) ans+=euler(i); //如果n/i为公约数
                }
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

    (3)BZOJ2818 Gcd

    题目链接

    (gcd(x,y)=k),则有 (gcd(xdiv k,ydiv k)=1)

    此时我们只需求区间 ([1,ndiv p_i]) 内有多少个互质的 ((x,y)) 即可。

    ((x,y))((y,x)) 视为两个不同的二元组,所以只需求出 ((x,y)) 即可,之后再 ( imes 2)。可是,如果按上述算法计算,((1,1)) 会重复算两遍,所以要减去 (1)

    code:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    const int N=10000005;
    int n,p,cnt,phi[N],pri[N];
    bool book[N]; 
    ll ans,sum[N];
    void getphi() //筛法求φ(1~n)顺便求1~n的所有质数
    {
    	phi[1]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(!book[i])
    		{
    			phi[i]=i-1;
    			pri[++cnt]=i;
    		}
    		for(int j=1;j<=cnt && i*pri[j]<=n;j++)
    		{
    			book[i*pri[j]]=1;
    			if(i%pri[j]==0)
    			{
    				phi[i*pri[j]]=phi[i]*pri[j];
    				break;
    			}
    			else phi[i*pri[j]]=phi[i]*phi[pri[j]];
    		}
    	}
    }
    int main()
    {
    	scanf("%d",&n);
    	getphi();
    	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+phi[i];
    	for(int i=1;i<=cnt;i++) ans+=sum[n/pri[i]]*2-1;
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    c_hhc_互补二元组(延迟计数+简单公式变换)
    b_vj_Hamiltonian Cycle(记忆化+位运算各种技巧)
    b_lq_分考场(尽量安排到旧考场)
    线程停止
    Lambda表达式
    静态代理演示
    多线程模拟龟兔赛跑
    Runnable和Thread
    线程网图下载
    接口
  • 原文地址:https://www.cnblogs.com/juruo-zzt/p/12306317.html
Copyright © 2011-2022 走看看