欧拉函数

欧拉函数

1-n中与n互质的数的个数

//单个数的欧拉函数值
int phi(int n){
    int ans=n;
    for(int i=2;i<=sqrt(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;
}
//欧拉函数打表
//非线性打表
const int maxm=1e6+5;
int phi[maxm];
void init(){
    for(int i=1;i<maxm;i++){
        phi[i]=i;
    }
    for(int i=2;i<maxm;i++){
        if(phi[i]==i){//数值等于本身的是素数
            for(int j=i;j<maxm;j+=i){
                phi[j]=phi[j]/i*(i-1);
            }
        }
    }
}
//欧拉函数线性筛
const int maxm=1e6+5;
int notprime[maxm];
int prime[maxm],cnt;
int phi[maxm];
void Pinit(){
    phi[1]=1;
    for(int i=2;i<maxm;i++){
        if(!notprime[i]){//如果是素数
            prime[cnt++]=i;
            phi[i]=i-1;
        }
        for(int j=0;j<cnt;j++){
            if(prime[j]*i>=maxm)break;
            notprime[prime[j]*i]=1;
            phi[prime[j]*i]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
            if(i%prime[j]==0)break;
        }
    }
}
性质:

1.如果p是素数,则φ(p )=p-1,反之,若φ(p )=p-1,则p一定是质数

2.如果p是素数,a是一个正整数,那么φ(pa)=pa-pa-1
证明:φ(pa)=pa-(前pa个数中中和pa不互质的数),因为pa只有一个质因子p,所以前pa个数中和pa不互质的数为p的倍数,这样的数为kp(1<=k<=pa-1),共有pa/p个,也就是pa-1个,所以φ(pa)=pa-pa-1

3.n=p1a1p2a2…pkak,那么φ(n)=n(1-1/p1)(1-1/p2)…(1-1/pk)

4.n= ∑d|n φ(d) (d包括1和n)

5.若m,n互质,则φ(mn) = φ(m) φ(n) (积性函数)

6.若n为奇数,φ(2n) = φ(n),因为2和奇数互质,结合欧拉函数是积性函数的性质即可得出这一结论

7.给定整数n,所有小于n且与n互质的数的和是 n∗φ(n)/2

8.若 gcd(n,i)=1 则 gcd(n,n-i)=1

待补充

在这里插入图片描述


hdu1286 找新朋友

题意:

给n,求1-n中与n互质的数的个数

思路:

模板题,求出n的欧拉函数值即可


bzoj2705 Longge的问题

题意:

求∑gcd(i,N) (1<=i<=N)。

思路:

设f(k)为满足gcd(m,n)=k,(1<=m<=n)的个数,则ans=∑(k*f(k)) (k为n的约数)
因为gcd(m,n)=k,所以gcd(m/k,n/k)=1,则f(k)=phi(n/k),phi为欧拉函数值
O(√n)枚举约数k按上式计算即可

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int phi(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;
}
signed main(){
    int n;
    while(cin>>n){
        int ans=0;
        for(int i=1;i*i<=n;i++){
            if(n%i==0){
                ans+=i*phi(n/i);
                if(i*i!=n)ans+=(n/i)*phi(i);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

hdu2588 GCD

题意:

给n,m,求有多少个x,满足1<=x<=n且gcd(x,n)>=m

思路:

和bzoj2705原理差不多,
设gcd(x,n)=k,
则gcd(x/k,n/k)=1,x的数量就是欧拉函数值phi(n/k)
注意这题只有满足k>=m的时候才累加进答案
O(√n)枚举因子k即可

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int phi(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;
}
signed main(){
    int T;
    cin>>T;
    while(T--){
        int n,m;
        cin>>n>>m;
        int ans=0;
        for(int i=1;i*i<=n;i++){
            if(n%i==0){
                if(i>=m){
                    ans+=phi(n/i);
                    if(i*i!=n){
                        if(n/i>=m){
                            ans+=phi(i);
                        }
                    }
                }else{
                    if(n/i>=m){
                        ans+=phi(i);
                    }
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

hdu2824 The Euler function

题意:

给l,r求phi[l]+phi[l+1]…+phi[r]

思路:

考察欧拉函数打表,区间和用前缀和来计算
但是这题内存限制很严格,数据范围在3e6,给的内存只能开一个一维数组
所以只能非线性打表(线性需要多数组),前缀和也要在phi数组上做,即phi[i]+=phi[i-1]


hdu4983 Goffi and GCD

题意:

给n,k,求有多少组a,b满足gcd(n-a,n)*gcd(n-b,n)=nk,答案对1e9+7取模

思路:

当a等于0的时候gcd(n-a,n)为最大值n
当b等于0的时候gcd(n-b,n)为最大值n
所以当a,b都为0的时候gcd(n-a,n)*gcd(n-b,n)等于最大值n2
所以:
1.当k大于2时,无解的,方案数为0
2.当k等于2时,只有a=0,b=0一组解,方案数为1
3.当k等于1时,设gcd(n-a,n)=x,则gcd(n-b,n)=n/x
gcd((n-a)/x,n/x)=1,gcd((n-b)/(n/x),n/(n/x))=1
a有phi(n/x)种,b有phi(x)种
相互匹配,则方案数为phi(n/x)*phi(x)种
又因为ab可以互换,所以还要再乘上2,即phi(n/x)*phi(x)*2
但是当n/x==x的时候不用乘2
O(√n)枚举n的因子x即可

最后一个坑点就是当n等于1的时候,这时候无论k为多少nk都为1
所以要特判n等于1的情况

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int phi(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;
}
signed main(){
    const int mod=1e9+7;
    int n,k;
    while(cin>>n>>k){
        if(n==1){
            cout<<1<<endl;
            continue;
        }
        if(k>2){
            cout<<0<<endl;
        }else if(k==2){
            cout<<1<<endl;
        }else{
            int ans=0;
            for(int i=1;i*i<=n;i++){
                if(n%i==0){
                    if(n/i!=i){
                        ans+=phi(n/i)*phi(i)*2;
                        ans%=mod;
                    }else{
                        ans+=phi(n/i)*phi(n/i);
                        ans%=mod;
                    }
                }
            }
            cout<<ans<<endl;
        }
    }
    return 0;
}

UVA11426 GCD - Extreme (II)

题意:

在这里插入图片描述

思路:

因为题目中有两个sigma,想办法把它拆开
设f(n)=gcd(1,n)+gcd(2,n)+…gcd(n-1,n) ;这是把第二个sigma的j固定为n之后第一个sigma的值
则G(n)=f(2)+f(3)+…f(n) ; 这是题目要求的G
显然G函数满足前缀和性质,因此很好处理
所以这题主要问题是在求f函数上

设g(x,n)=k,则g(x/k,n/k)=1,因此满足条件的k有phi(n/k)个则f(n)+=phi(n/k)*k
所以对于某一个n,O(√n)枚举n的因子k即可求出f(n)

但是如果对于每一个n=1,2,3…我们都这样算,显然超时
一个好的办法就是利用埃筛的思想,对于每一个数k,把k的所有倍数n加上phi(n/k)*k
详见代码

求出f()之后对其求前缀和即为G(),对于每个输入的n,可以做到O(1)输出结果

ps:
总结:多个sigma可以尝试拆开,其中某些sigma可能可以变成上一个sigma的前缀和

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=4e6+5;
int phi[maxm];
int f[maxm];
void init(){//预处理欧拉函数
    for(int i=1;i<maxm;i++){
        phi[i]=i;
    }
    for(int i=2;i<maxm;i++){
        if(phi[i]==i){
            for(int j=i;j<maxm;j+=i){
                phi[j]=phi[j]/i*(i-1);
            }
        }
    }
}
void init2(){//预处理f[]
    for(int i=1;i<maxm;i++){
        for(int j=i+i;j<maxm;j+=i){
            f[j]+=phi[j/i]*i;
        }
    }
    for(int i=3;i<maxm;i++){//f[]的前缀和即G[]
        f[i]+=f[i-1];
    }
}
signed main(){
    init();
    init2();
    int n;
    while(cin>>n&&n){
        cout<<f[n]<<endl;
    }
    return 0;
}

bzoj2818 Gcd

题意:

给n,求有多少对x,y
1<=x,y<=n满足gcd(x,y)是素数

思路:

考虑每一个质数p对答案的贡献
gcd(x,y)=p,则gcd(x/p,y/p)=1
因为x,y<=n,所以x/p,y/p<=n/p
设a=x/p,b=y/p,则问题即求gcd(a,b)=1且1<=a,b<=n/p
因此答案就是遍历小于等于n的每个p,求出1<=a,b<=n/p中gcd(a,b)=1的对数

那么怎么求1<=x<=n中互质对数呢?
其实就是phi数组的前n项前缀和
但是因为(a,b)!=(b,a) (a!=b),也就是数对有序,所以a!=b的时候答案要乘2,
a=b的时候不用乘2,a=b的情况只有一种:(1,1)
设phi的前缀和数组为sum,则对于每个p的答案为sum(n/p)*2+1
遍历n以内的所有素数p,ans+=sum(n/p)*2+1即可

可以优化一下空间,不用sum数组直接在phi数组上求前缀和,即phi[i]+=phi[i-1]

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e7+5;
int notprime[maxm];
int prime[maxm],cnt;
int phi[maxm];
void init(int n){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!notprime[i]){
            prime[cnt++]=i;
            phi[i]=i-1;
        }
        for(int j=0;j<cnt;j++){
            if(prime[j]*i>n)break;
            notprime[prime[j]*i]=1;
            phi[prime[j]*i]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
            if(i%prime[j]==0)break;
        }
    }
    for(int i=1;i<=n;i++){//直接在原数组上求前缀和
        phi[i]+=phi[i-1];
    }
}
signed main(){
    int n;
    cin>>n;
    init(n);
    int ans=0;
    for(int j=0;j<cnt;j++){
        ans+=phi[n/prime[j]]*2-1;
    }
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值