《数论及应用》第6章 乘性函数问题(上)

来源:互联网 发布:js复制对象副本 编辑:程序博客网 时间:2024/06/05 09:10

1.欧拉函数:

关于欧拉函数,求的是指不超过n的与n互素的正整数的个数,按一般思想就是对数n进行素因子分解之后,所有的素数幂的欧拉函数之积

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

推论:若n是奇数,φ(2n)=φ(n)

两个常用定理:

欧拉定理:对任何两个互质的正整数a,m(m>=2)有a^φ(m)≡1(mod m)

费马小定理:当m是质数的时候,a^(m-1)≡1(mod m)

欧拉函数的算法实现:

//递归求欧拉#include <stdio.h>#define maxn 10000int phi[maxn];//个人觉得这个时间比较慢,//而且你懂的,数组的大小是有限的,//如果是求大数的就不行了void Phi(){    for(int i=1;i<=maxn;i++)        phi[i]=i;    for(int i=2;i<=maxn;i+=2)        phi[i]/=2;    for(int i=3;i<=maxn;i+=2)        if(phi[i]==i)            for(int j=i;j<=maxn;j+=i)                phi[j]=phi[j]/i*(i-1);}

然后是一种单独求欧拉值的:

//单独求欧拉函数#include <stdio.h>#include <math.h>//如果必要,可以改成long longunsigned euler(unsigned x){    unsigned i,res=x;    for(i=2;i<=sqrt(x*1.0);i++)        if(x%i==0)        {            res=res/i*(i-1);            while(x%i==0)            x/=i;        }    if(x>1)    res=res/x*(x-1);    return res;}

还有一种素数表实现欧拉值的写法:

//传说这貌似时间最少//用是时间是O(n) n是sqrt(x)内的素数的个数//写起来好像跟前面的没什么很大的区别#include <stdio.h>#include <string.h>int prime[50000];int p[20000];void Prime(){    memset(prime,1,sizeof(prime));    prime[0]=prime[1]=0;    for(int i=2;i*i<=50000;i++)    if(prime[i])    for(int j=2*i;j<=50000;j+=i)    prime[j]=0;    int k=0;    for(int i=0;i<=50000;i++)    {        if(prime[i])        p[++k]=i;    }}int phi(int n){    int res=n;    for(int i=1;p[i]*p[i]<=n;i++)    if(n%p[i]==0)    {        res=res-res/p[i];        do        n/=p[i];        while(n%p[i]==0);    }    if(n>1)    res=res-res/n;    return res;}

欧拉函数的应用:

http://poj.org/problem?id=2407

就是模板题,直接求出欧拉值

水题

#include <stdio.h>long long euler(long long x){    long long i,res=x;    for(i=2;i*i<=x;i++)      if(x%i==0)      {          res=res/i*(i-1);          while(x%i==0)          x/=i;      }    if(x>1)    res=res/x*(x-1);    return res;}int main(){    long long n;    while(scanf("%I64d",&n)!=EOF,n)    {        printf("%I64d\n",euler(n));    }    return 0;}

http://poj.org/problem?id=1284

求原根

所谓原根指的是http://zh.wikipedia.org/wiki/原根  反正是不好玩的东西

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

http://poj.org/problem?id=2478

还是欧拉函数的应用,求的是从2到n的欧拉值之和

这个题REWA TLE MLE 多次。。。RE是因为没有注意到数据类型,数组开大了就MLE了,然后换了筛素数的方法求欧拉值结果WA了,改对之后又是RE了   唉   伤不起啊    给一个水题坑死了

算了  坑就坑吧  把试过的几次代码都贴上

//这个时间用了235ms#include <cstdlib>#include <iostream>using namespace std;#define maxn 1000001long long a[maxn];int main(){    long long i,j;    for(i=1;i<=maxn;i++) a[i]=i;    for(i=2;i<=maxn;i+=2) a[i]/=2;    for(i=3;i<=maxn;i+=2) if(a[i]==i)    {        for(j=i;j<=maxn;j+=i)            a[j]=a[j]/i*(i-1);    }    for(i=3;i<maxn;i++)    a[i]+=a[i-1];    long long n;    while(cin>>n&&n)    cout<<a[n]<<endl;    return 0;}

//这个时间用了79ms#include <iostream>#include <cstdio>using namespace std;#define maxn 1000001bool isprime[maxn];long long prime[maxn];long long phi[maxn];long long ans[maxn];void getprime(){    long long k=0;    for(int i=0;i<=maxn;i++)    isprime[i]=1;    for(int i=2;i<=maxn;i++)    {        if(isprime[i])        {            prime[k++]=i;            phi[i]=i-1;        }        for(int j=0;j<k&′[j]*i<=maxn;j++)        {            isprime[i*prime[j]]=0;            if(i%prime[j]!=0)            {                phi[i*prime[j]]=phi[i]*phi[prime[j]];            }            else            {                phi[i*prime[j]]=phi[i]*prime[j];                break;            }        }    }}int main(){    getprime();    ans[1]=0;    for(int i=2;i<=maxn;i++)    ans[i]=ans[i-1]+phi[i];    int n;    while(scanf("%d",&n),n)    {        printf("%I64d\n",ans[n]);    }    return 0;}

//这个用了94ms#include <iostream>#include <cstdio>using namespace std;#define maxn 1000001bool isprime[maxn];long long prime[maxn];long long phi[maxn];long long ans[maxn];void getprime(){    long long k=0;    for(int i=0;i<=maxn;i++)    isprime[i]=1;    for(int i=2;i<=maxn;i++)    {        if(isprime[i])        {            prime[k++]=i;            phi[i]=i-1;        }        for(int j=0;j<k&′[j]*i<=maxn;j++)        {            isprime[i*prime[j]]=0;            if(i%prime[j]!=0)            {                phi[i*prime[j]]=phi[i]*phi[prime[j]];            }            else            {                phi[i*prime[j]]=phi[i]*prime[j];                break;            }        }    }}int main(){    getprime();    ans[1]=0;    for(int i=2;i<=maxn;i++)    ans[i]=ans[i-1]+phi[i];    int n;    while(scanf("%d",&n),n)    {        printf("%I64d\n",ans[n]);    }    return 0;}


http://poj.org/problem?id=3090

根据理解将平面划分为两个区域,中间那一线的当然只看得到最近的点(1,1) 至于其他的就只看得到,与输入的n值所互质的数的个数,总的意思还是求1到n的欧拉值之和

#include <stdio.h>#define maxn 10000int phi[maxn];int main(){    for(int i=1;i<=maxn;i++)        phi[i]=i;    for(int i=2;i<=maxn;i+=2)        phi[i]/=2;    for(int i=3;i<=maxn;i+=2)    {        if(phi[i]==i)        {            for(int j=i;j<=maxn;j+=i)            phi[j]=phi[j]/i*(i-1);        }    }    for(int i=2;i<=maxn;i++)    phi[i]+=phi[i-1];    int n,t;    scanf("%d",&t);    int tt=0;    while(t--)    {        scanf("%d",&n);        printf("%d %d %d\n",++tt,n,2*phi[n]+1);    }    return 0;}