• 浅谈欧拉函数


    笔者太菜,现在才学欧拉函数还学得不咋滴……

    定义#

    定义:(φ(n))是小于等于(n),且与(n)互质的数的个数。

    写成另一个式子就是:(∑_{i=1}^{n} 1[gcd(i,n)==1])

    有一个结论,是(φ(n))为积性函数,即有(φ(ab)=φ(a)*φ(b)).

    还有一些其它性质:当(p)为质数时,显然有(φ(p)=p-1).

    (p|n)(p^2 | n),则(φ(n)=φ(n/p)*p)

    (p|n) 且不满足(p^2 | n),则(φ(n)=φ(n/p)*(p-1))

    还有:(∑_{d|n} φ(d)=n)

    由于它是积性函数,于是我们可以用欧拉筛把它筛出来。

    (p=prime)时,用第一个性质。

    (p)(x)互质时,则(φ(x*p)=φ(x)*φ(p))

    (x)(p)不互质时,就有:

    (x=t*p^k)

    则有:(φ(x*p)=φ(t*p^{k+1})=φ(t*p^k)*p=φ(x)*p)

    由此可以写出欧拉筛程序:

    void Init(){
    	phi[1]=1;
    	for(int i=2;i<=MAXN;++i){
    		if(is_prime[i])phi[i]=i-1,p[++tot]=i;
    		for(int j=1;j<=tot&&i*p[j]<=n;++j){
    			is_prime[i*p[j]]=0;
    			if(i%p[j]==0){
    				phi[i*p[j]]=phi[i]*p[j];
    				break;
    			}
    			else phi[i*p[j]]=phi[i]*phi[p[j]];
    		}
    	}
    }
    

    继续普及定理:

    欧拉定理:当(x)(p)互质时,有:

    (x^{φ(m)}≡1(mod p))

    这个东西的局限性有点大,来看一下拓展欧拉定理:

    无需(x)(p)互质,当(y>φ(p))时,有:

    $ x^y$ (≡) (x^{b%φ(p)+φ(p)}) ((mod) (p))

    当$y<φ(p)时,指数上没有那个+φ(p)

    根据这个东西,我们可以解决一些问题,诸如:

    (2^{2^{2^...}}) (mod) (p)的东西(上帝与集合的正确用法)

    来一个题目:求(x,y<=n)(gcd(x,y)∈prime)的数对个数。

    看向文章第一个式子,(∑_{i=1}^{n} 1[gcd(i,n)==1])

    自然联想到欧拉函数。

    (gcd)变一下,(gcd(x,y)=1) (->) (gcd(x*p,y*p)=p(p∈prime))

    那么,我们就想求(p∈prime)(p<=n)

    自然一遍筛法。而且可以顺便把欧拉函数筛出来。

    对于一个数对((a,b)),设(a<=b),

    那么(a)(φ(p))个值可以使(gcd(a,b)=1).

    那么我们可以做一下前缀和。

    答案就变为:枚举所有(p<=n)且p为质数,由于没有大小限制所以最后答案(*2);注意枚举时的重复,注意欧拉函数统计1时所计算的重复,所以有(-1).

    (Code:)

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int MAXN=1e7+10;
    int phi[MAXN],n,tot,p[MAXN],flg[MAXN];
    long long Ans,sum[MAXN];
    void Init(int MAXN1){
    	phi[1]=1;
    	/*for(int i=2;i<=MAXN1;++i){
    		if(!phi[i])
    			prime[++tot]=i;
    			for(int j=i;j<=MAXN1;j+=i){
    				if(!phi[j])phi[j]=j;
    				phi[j]=phi[j]/i*(i-1);
    			}
    	}*/
    	phi[1]=1;
        for(int i=2;i<=n;++i) {
            if(!flg[i]) p[++tot]=i,phi[i]=i-1;
            for(int j=1;j<=tot&&i*p[j]<=n;++j) {
                flg[i*p[j]]=1;
                if(i%p[j]==0) {
                    phi[i*p[j]]=phi[i]*p[j];
                    break;
                } else {
                    phi[i*p[j]]=phi[i]*phi[p[j]];
                }
            }
        }
    	for(int i=1;i<=MAXN;++i)sum[i]=sum[i-1]+phi[i];
    }
    int main(){
    	scanf("%d",&n);
    	Init(n+10);
    	for(int i=1;i<=tot&&p[i]<=n;++i)Ans+=sum[n/p[i]]+sum[n/p[i]]-1;
    	printf("%lld
    ",Ans);
    	return 0;
    }
    

  • 相关阅读:
    委托(delegate)的三种调用方式:同步调用,异步调用,异步回调(转载)
    C#异步:实现一个最简单的异步
    关于Thread.IsBackground属性的理解(转载)
    C# 中的多线程(转载)
    个人对AutoResetEvent和ManualResetEvent的理解(转载)
    C#线程系列讲座(4):同步与死锁
    Nginx location 配置踩坑过程分享
    微信扫码登录网页实现原理
    负载均衡SLB
    Tomcat学习
  • 原文地址:https://www.cnblogs.com/h-lka/p/11600309.html
Copyright © 2020-2023  润新知