欧拉函数

1.首先明确积性函数
对于算术函数f(x)如果如果对于任意两个互质的正整数n,m,有f(nm)=f(n)*f(m),那么称该函数为积性函数。
如果对任意两个正整数n,m都满足f(nm)=f(n)*f(m),那么称为完全积性函数。
2.欧拉函数的概念
φ(n);表示不大于n,并且和n互质的数的个数(包括1)。
φ(1)=1;
3.
定理1
如果f(x)为积性函数,通过质因子分解可以表示为
x=p1^a1+ p2 ^a2+p3 ^a3…+pn ^an
那么f(x)=f(p1^a1)f(p2 ^a2)…f(pn ^an),
根据积性函数的定义容易得到。

定理2
通过质因子分解后,
n=p1^a1p2 ^a2p3 ^a3*…pk ^ak
欧拉函数的计算公式为,
φ(n)=n
(1-1/p1) * (1-1/p2)(1-1/pk)
理解:
对于n以内p1的倍数是均匀分布的,所以1~p1内p1的倍数所占的比例是1/p1,又因为n为p1的整数倍,所以1 ~ n内p1的倍数的比例也为1/p1,所以1 ~n 内不是p1的倍数的数的个数为 n*(1-1/p1)。
同理,其余均是如此。
连续相乘后的结果就表示1~n中,不是p1,p2…pk中任意一个数的倍数的数的个数,即与n互质的数的个数。
推论:
当n为奇数时,φ(2*n)=φ(n)

定理3
如果p为素数,那么φ(p )=p-1。
反之也成立,二者互为充要条件。
φ ( p)=p*(1-1/p)=p-1。

定理4:
设p为素数,a是一个正整数,那么φ(p^a)=p ^a-p ^(a-1).

定理5:
设n,m是两个互素的正整数,那么φ(nm)=φ(n)*φ(m)。
积性函数的性质

定理6:
对于一个大于2的正整数x,φ(x)为偶数。

4.欧拉定理:
对于两个互质的正整数a,m(m>=2),有a^φ(m)=1(mod m)
特殊情况:
m为素数时,有费马小定理 a^(m-1)=1(mod m)

5.如何求欧拉函数
(1),对单个数求O(sqrt(n))
基本思想是:欧拉函数的计算公式。质因子分解。

#include <bits/stdc++.h>
using namespace std;
int euler_fun(int x)
{
    int res=x;
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        printf("%d\n",euler_fun(n));
    }
    return 0;
}

(2)线性筛欧拉函数(欧拉筛筛欧拉函数)
首先要对欧拉筛有了解,同样是利用欧拉函数的性质和计算公式

void euler(int n)
{
	phi[1]=1;//1要特判 
	for (int i=2;i<=n;i++)
	{
		if (flag[i]==0)//这代表i是质数 
		{
			prime[++num]=i;
			phi[i]=i-1;
		}
		for (int j=1;j<=num&&prime[j]*i<=n;j++)//经典的欧拉筛写法 
		{
			flag[i*prime[j]]=1;//先把这个合数标记掉 
			if (i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];//若prime[j]是i的质因子,
				//则根据计算公式,i已经包括i*prime[j]的所有质因子 
				break;//经典欧拉筛的核心语句,这样能保证每个数只会被自己最小的因子筛掉一次 
			}
			else phi[i*prime[j]]=phi[i]*phi[prime[j]];//利用了欧拉函数是个积性函数的性质 
		}
	}
}

当i%prime[j]==0时,iprime[j]和i的质因子相同,i是prime[j]的倍数,根据计算公式,只要把公式的第一项n换成iprime[j]即可,其它的不变,因此phi[i*prime[j]]=phi[i]*prime[j]。

(3)递推求欧拉函数O(nln n)
基本思想:利用上述定理3,利用素数。首先把所有的数的欧拉函数值赋本身,然后从小到大进行遍历,如果一个数的欧拉函数的值=这个数本身,那么这个数为一个素数,对它进行处理,同时对它的倍数根据计算公式进行处理,即可。

#include <bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int phi[N];
void euler()
{
    for(int i=1;i<N;i++)
        phi[i]=i;//一开始赋初值
    for(int i=2;i<N;i++)//遍历
    {
        if(phi[i]==i)//i为素数
        {
            for(int j=i;j<N;j+=i)//对范围内的i的倍数进行处理
                phi[j]=phi[j]/i*(i-1);
        }
    }
}
int main()
{
    euler();
    for(int i=1;i<=100;i++)
        printf("%d:%d\n",i,phi[i]);
    return 0;
}

poj2407
poj1284
poj2478(模板题)

#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int phi[N];
bool vis[N];
vector<int>prime;
void euler()
{
    prime.clear();
    memset(vis,false,sizeof(vis));
    for(int i=2;i<=N-5;i++)
    {
        if(!vis[i])
        {
            prime.push_back(i);
            phi[i]=i-1;
        }
        for(int j=0;j<prime.size()&&i*prime[j]<=N-5;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else
                phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}
int main()
{
    int n;
    euler();
    while(scanf("%d",&n),n)
    {
        ll ans=0;
        for(int i=2;i<=n;i++)
            ans+=phi[i];
        printf("%lld\n",ans);
    }
    return 0;
}

poj3090
关键在于发现欧拉函数的应用。
可以在纸上画出前几个图,发现,可以画边的点的纵横坐标的关系为欧拉函数的关系(只看水平方向),即横坐标为于纵坐标互质的数。(n=1时除外),这样就可以转化为欧拉函数求解,最后只要求和,然后加上竖直方向即可。

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N=1e3+5;
int phi[N];
vector<int>prime;
bool vis[N];
void euler()
{
    memset(vis,false,sizeof(false));
    prime.clear();
    for(int i=2;i<=N-5;i++)
    {
        if(!vis[i])
        {
            prime.push_back(i);
            phi[i]=i-1;
        }
        for(int j=0;j<prime.size()&&i*prime[j]<=N-5;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else
                phi[i*prime[j]]=phi[i]*phi[prime[j]];
        }
    }
}
int main()
{
    int n,c;
    euler();
    scanf("%d",&c);
    for(int i=1;i<=c;i++)
    {
        scanf("%d",&n);
        long long ans=3;
        for(int j=2;j<=n;j++)
            ans+=(2*phi[j]);
        printf("%d %d %lld\n",i,n,ans);
    }
    return 0;
}

poj3696
大概思路:
利用(10^p - 1)8/9刚好是全为8的数字,所以有方程(10 ^p -1)8/9= L x
所以,10^p -1=L
x
9/8;
令m=9
L/gcd(8,L),
则 10^p -1=m*x
所以有10^p=1(mod m)
由欧拉定理,有当p=φ(m)时,方程成立。
有因为要求最小的解,所以对φ(m)进行质因子分解,对于某一个质因子a,进行p/=a,直到把p代入方程不成立或p%a!=0,结束。
对所有的质因子进行这样的遍历。

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1e3+5;
ll n,m;
ll p[N]={0};
ll gcd(ll a,ll b)
{
    return (b==0)?a:gcd(b,a%b);
}
ll euler(ll x)
{
    ll res=x;
    for(ll i=2;i*i<=x;i++)//这个地方如果用int,会超时
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
ll mul(ll a,ll b)
{
    ll res=0;
    a%=m;
    b%=m;
    while(b)
    {
        if(b&1)
        {
            res+=a;
            if(res>=m)
                res-=m;
        }
        a<<=1;
        if(a>=m)
            a-=m;
        b>>=1;
    }
    if(res>=m)
        res-=m;
    return res;
}
ll f_pow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=mul(res,a)%m;
        a=mul(a,a)%m;
        b>>=1;
    }
    return res%m;
}
void fun(ll x)
{
    p[0]=0;
    ll t=x;
   for(ll i=2;i*i<=x;i++)
   {
       if(t%i==0)
       {
           p[++p[0]]=i;;
           while(t%i==0)
                t/=i;
       }
   }
   if(t>1)
     p[++p[0]]=t;
}
void solve(ll x)
{
    fun(x);//求出所有质因子
    for(int i=1;i<=p[0];i++)
    {//printf(" %d\n",p[i]);
        while(1)
        {
            x/=p[i];
            if(f_pow(10,x)!=1)
            {
                x*=p[i];
                break;
            }
            else if(x%p[i])
                break;
        }
    }
    printf("%lld\n",x);
}
int main()
{
    int cas=0;
    while(scanf("%lld",&n),n)
    {
        printf("Case %d: ",++cas);
        m=n/gcd(n,8)*9;
        if(gcd(10,m)!=1)
            puts("0");
        else
            solve(euler(m));
    }
    return 0;
}

poj3358
首先要知道如何把小数部分转化为二进制表示,
对小数部分不断乘2,直到整数位大于1,此时二进制取1,然后舍弃整数位,继续对小数位操作。
如:
0.1:
0.12=0.2 0
0.2
2=0.4 0
0.42=0.8 0
0.8
2=1.6 1
0.62=1.2 1
0.2
2=0.4 0

在这里插入图片描述

#include <cstdio>
#include <cstring>
using namespace std;
int p,q,mod;
int fac[1005]={0};
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int euler(int x)
{
    int res=x;
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
void factor(int a)
{
    int ta=a;
    fac[0]=0;
    for(int i=2;i*i<=a;i++)
    {
        if(ta%i==0)
        {
            fac[++fac[0]]=i;
            while(ta%i==0)
                ta/=i;
        }
    }
    if(ta>1)
        fac[++fac[0]]=ta;
}
int m_pow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1)
            res=(long long)res*a%mod;
        a=(long long)a*a%mod;//wa会超int
        b>>=1;
    }
    return res%mod;
}
int solve(int x)
{
    factor(x);
    for(int i=1;i<=fac[0];i++)
    {
        while(1)
        {
            x/=fac[i];
            if(m_pow(2,x)!=1)
            {
                x*=fac[i];
                break;
            }
            else if(x%fac[i])
                break;
        }
    }
    return x;
}
int main()
{
    int cas=0;
    while(scanf("%d/%d",&p,&q)!=EOF)
    {
        int d=gcd(p,q);
        int p1=p/d;
        int q1=q/d;
        int pos=0;//开始位置
        while(q1%2==0)
        {
            q1/=2;
            pos++;
        }
        mod=q1;
        int x=euler(q1);//循环节为x的质因子
        printf("Case #%d: %d,%d\n",++cas,pos+1,solve(x));
    }
    return 0;
}

更多题目
hdu3501
自己推了好久,还是一直错!
借鉴别人的思路。自己还是想不到。
在这里插入图片描述

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll euler(ll x)
{
    ll res=x;
    for(int i=2;(ll)i*i<=x;i++)
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
int main()
{
    ll n;
    while(scanf("%lld",&n),n)
    {
        ll ans=(n-1)*n/2;
        printf("%lld\n",(ans-(n*euler(n)/2))%mod);
    }
    return 0;
}

hdu2588
要求[1,n]中与n的gcd>=m的数的个数,
令gcd(n,x)=a;
其中n=ab,x=ac。
有(b,c)=1,否则(n,x)!=a。
因而只要让a>=m,求出x的个数,即可。
即求满足a>=m的b的欧拉函数的和

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
int n,m;
int euler(int x)
{
    int res=x;
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
int solve()
{
    int tn=n;
    int ans=0;
    for(int i=1;i*i<=n;i++)
    {
        if(n%i==0)
        {
            if(i>=m)
                ans+=euler(n/i);
            if(n/i>=m&&(n/i)!=i)
                ans+=euler(i);
        }
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        printf("%d\n",solve());
    }
    return 0;
}

Star SPOJ - STARSBC
phi[n]/2
用int会超时,改成long long 就过了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll euler(ll x)
{
    ll res=x;
    for(ll i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
int main()
{
    ll n;
    while(scanf("%lld",&n)==1)
    {
        printf("%lld\n",euler(n)/2);
    }
    return 0;
}

hdu4983
在这里插入图片描述大概思路就是对x的可能取值,进行分析,以使得(n-a)/x和x互质。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
ll n,k;
ll euler(ll x)
{
    ll res=x;
    for(ll i=2;i*i<=x;i++)
    {
        if(x%i==0)
        {
            res-=(res/i);
            while(x%i==0)
                x/=i;
        }
    }
    if(x>1)
        res-=(res/x);
    return res;
}
ll solve(ll x)
{
    ll ans=0;
    for(ll i=1;i*i<=x;i++)
    {
        if(x%i==0)
        {
            ll ta=euler(i);
            if(i*i==x)
                ans+=(ta*ta);
            else
                ans+=(2*ta*euler(x/i));
        }
        ans%=mod;
    }
    return ans;
}
int main()
{
    while(scanf("%lld%lld",&n,&k)!=EOF)
    {
        if(n==1)//特判,易错
            puts("1");
        else if(k>2)
            puts("0");
        else if(k==2)
            puts("1");
        else
            printf("%lld\n",solve(n)%mod);
    }
    return 0;
}

hdu5152
线段树+欧拉函数
以后再补

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值