数论-模板

1.扩展的欧几里德定理

  1. //拓展欧几里得定理,求ax+by=gcd(a,b)的一组解(x,y),d=gcd(a,b)
  2. void gcd(int a,int b,int &d,int &x,int &y)
  3. {
  4. if(!b){d=a;x=1;y=0;}
  5. else{gcd(b,a%b,d,y,x);y-=x*(a/b);}
  6. }
//拓展欧几里得定理,求ax+by=gcd(a,b)的一组解(x,y),d=gcd(a,b)
void gcd(int a,int b,int &d,int &x,int &y)
{
    if(!b){d=a;x=1;y=0;}
    else{gcd(b,a%b,d,y,x);y-=x*(a/b);}
}

用法1:

求ax+by=c的整数解。ax+by=gcd(a,b)=g的一组解为(x0,y0),则ax+by=c的一组解为(x0*c/g,y0*c/g)。当c不是g的倍数时无整数解

若(x1,y1)是ax+by=c的一组解,则其任意整数解为(x1+k*bb,y1-k*aa),其中aa=a/gcd(a,b),bb=b/gcd(bb),k为任意整数

2.求模乘法的逆

  1. //求得a在模n条件下的逆
  2. int inv(int a,int n)
  3. {
  4. int d,x,y;
  5. gcd(a,n,d,x,y);
  6. return d==1?(x+n)%n:-1;
  7. }
//求得a在模n条件下的逆
int inv(int a,int n)
{
   int d,x,y;
   gcd(a,n,d,x,y);
   return d==1?(x+n)%n:-1;
}

或则:

  1. v=pow_mod(a,n-m-1,n);//n为素数,pow_mod(a,n-1,n)=1,费马小定理。所以a^m*a^(n-m-1)=a^(n-1)=1(mod n).
v=pow_mod(a,n-m-1,n);//n为素数,pow_mod(a,n-1,n)=1,费马小定理。所以a^m*a^(n-m-1)=a^(n-1)=1(mod n).

3.快速幂求解a^b

  1. //快速幂求a^b
  2. int pow_mod(int a,int b)
  3. {
  4. int s=1;
  5. while(b)
  6. {
  7. if(b&1)
  8. s=(s*a)%mod;
  9. a=(a*a)%mod;
  10. b=b>>1;
  11. }
  12. return s;
  13. }
//快速幂求a^b
int pow_mod(int a,int b)
{
    int s=1;
    while(b)
    {
        if(b&1)
            s=(s*a)%mod;
        a=(a*a)%mod;
        b=b>>1;
    }
    return s;
}

4.求解模方程a^x=b(mod n)。

1.n为素数。无解返回-1

  1. //求解模方程a^x=b(mod n)。n为素数,无解返回-1
  2. int log_mod (int a,int b,int n)
  3. {
  4. int m,v,e=1,i;
  5. m=ceil(sqrt(n+0.5));
  6. v=inv(pow_mod(a,m),n);
  7. map<int,int>x;
  8. x[1]=0;
  9. for(i=1;i<m;i++)
  10. {
  11. e=(e*a)%n;
  12. if(!x.count(e))x[e]=i;
  13. }
  14. for(i=0;i<m;i++)
  15. {
  16. if(x.count(b))return i*m+x[b];
  17. b=(b*v)%n;
  18. }
  19. return -1;
  20. }
//求解模方程a^x=b(mod n)。n为素数,无解返回-1
int log_mod (int a,int b,int n)
{
    int m,v,e=1,i;
    m=ceil(sqrt(n+0.5));
    v=inv(pow_mod(a,m),n);
    map<int,int>x;
    x[1]=0;
    for(i=1;i<m;i++)
    {
        e=(e*a)%n;
        if(!x.count(e))x[e]=i;
    }
    for(i=0;i<m;i++)
    {
        if(x.count(b))return i*m+x[b];
        b=(b*v)%n;
    }
    return -1;
}

2.n不是素数。

  1. //hdu 2815 Mod Tree
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <map>
  6. #include <iostream>
  7. #include <algorithm>
  8. using namespace std;
  9. #define LL __int64
  10. LL gcd(LL a,LL b)
  11. {
  12. return b==0?a:gcd(b,a%b);
  13. }
  14. //拓展欧几里得定理,求ax+by=gcd(a,b)的一组解(x,y),d=gcd(a,b)
  15. void gcd_mod(LL a,LL b,LL &d,LL &x,LL &y)
  16. {
  17. if(!b){d=a;x=1;y=0;}
  18. else{gcd_mod(b,a%b,d,y,x);y-=x*(a/b);}
  19. }
  20. //求解模方程d*a^(x-c)=b(mod n)。d,a和n互质,无解返回-1
  21. LL log_mod (LL a,LL b,LL n,LL c,LL d)
  22. {
  23. LL m,v,e=1,i,x,y,dd;
  24. m=ceil(sqrt(n+0.5)); //x=i*m+j
  25. map<LL,LL>f;
  26. f[1]=m;
  27. for(i=1;i<m;i++) //建哈希表,保存a^0,a^1,...,a^m-1
  28. {
  29. e=(e*a)%n;
  30. if(!f[e])f[e]=i;
  31. }
  32. e=(e*a)%n;//e=a^m
  33. for(i=0;i<m;i++)//每次增加m次方,遍历所有1<=f<=n
  34. {
  35. gcd_mod(d,n,dd,x,y);//d*x+n*y=1-->(d*x)%n=1-->d*(x*b)%n==b
  36. x=(x*b%n+n)%n;
  37. if(f[x])
  38. {
  39. LL num=f[x];
  40. f.clear();//需要清空,不然会爆内存
  41. return c+i*m+(num==m?0:num);
  42. }
  43. d=(d*e)%n;
  44. }
  45. return -1;
  46. }
  47. int main()
  48. {
  49. LL a,b,n;
  50. while(scanf("%I64d%I64d%I64d",&a,&n,&b)!=EOF)
  51. {
  52. if(b>=n)
  53. {
  54. printf("Orz,I can’t find D!\n");
  55. continue;
  56. }
  57. if(b==0)
  58. {
  59. printf("0\n");
  60. continue;
  61. }
  62. LL ans=0,c=0,d=1,t;
  63. while((t=gcd(a,n))!=1)
  64. {
  65. if(b%t){ans=-1;break;}
  66. c++;
  67. n=n/t;
  68. b=b/t;
  69. d=d*a/t%n;
  70. if(d==b){ans=c;break;}//特判下是否成立。
  71. }
  72. if(ans!=0)
  73. {
  74. if(ans==-1){printf("Orz,I can’t find D!\n");}
  75. else printf("%I64d\n",ans);
  76. }
  77. else
  78. {
  79. ans=log_mod(a,b,n,c,d);
  80. if(ans==-1)printf("Orz,I can’t find D!\n");
  81. else printf("%I64d\n",ans);
  82. }
  83. }
  84. return 0;
  85. }
  86. /*
  87. 求解模方程a^x=b(mod n),n不为素数。拓展Baby Step Giant Step
  88. 模板题。
  89. 方法:
  90. 初始d=1,c=0,i=0;
  91. 1.令g=gcd(a,n),若g==1则执行下一步。否则由于a^x=k*n+b;(k为某一整数),则(a/g)*a^k=k*(n/g)+b/g,(b/g为整除,若不成立则无解)
  92. 令n=n/g,d=d*a/g,b=b/g,c++则d*a^(x-c)=b(mod n),接着重复1步骤。
  93. 2.通过1步骤后,保证了a和d都与n互质,方程转换为d*a^(x-c)=b(mod n)。由于a和n互质,所以由欧拉定理a^phi(n)==1(mod n),(a,n互质)
  94. 可知,phi(n)<=n,a^0==1(mod n),所以构成循环,且循环节不大于n。从而推出如果存在解,则必定1<=x<n。(在此基础上我们就可以用
  95. Baby Step Giant Step方法了)
  96. 3.令m=ceil(sqrt(n)),则m*m>=n。用哈希表存储a^0,a^1,..,a^(m-1),接着判断1~m*m-1中是否存在解。
  97. 4.为了减少时间,所以用哈希表缩减复杂度。分成m次遍历,每次遍历a^m长度。由于a和d都与n互质,所以gcd(d,n)=1,
  98. 所以用拓展的欧几里德定理求得d*x+n*y=gcd(d,n)=1,的一组整数解(x,y)。则d*x+n*y=1-->d*x%n=(d*x+n*y)%n=1-->d*(x*b)%n=((d*x)%n*b%n)%n=b。
  99. 所以若x*b在哈希表中存在,值为k,则a^k*d=b(mod n),答案就是ans=k+c+i*m。如果不存在,则令d=d*a^m,i++后遍历下一个a^m,直到遍历a^0到a^(m-1)还未找到,则说明不解并退出。
  100. */
//hdu 2815 Mod Tree
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL __int64
LL gcd(LL a,LL b)
{
    return b==0?a:gcd(b,a%b);
}
//拓展欧几里得定理,求ax+by=gcd(a,b)的一组解(x,y),d=gcd(a,b)
void gcd_mod(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b){d=a;x=1;y=0;}
    else{gcd_mod(b,a%b,d,y,x);y-=x*(a/b);}
}
//求解模方程d*a^(x-c)=b(mod n)。d,a和n互质,无解返回-1
LL log_mod (LL a,LL b,LL n,LL c,LL d)
{
    LL m,v,e=1,i,x,y,dd;
    m=ceil(sqrt(n+0.5));     //x=i*m+j
    map<LL,LL>f;
    f[1]=m;
    for(i=1;i<m;i++)  //建哈希表,保存a^0,a^1,...,a^m-1
    {
        e=(e*a)%n;
        if(!f[e])f[e]=i;
    }
    e=(e*a)%n;//e=a^m
    for(i=0;i<m;i++)//每次增加m次方,遍历所有1<=f<=n
    {
        gcd_mod(d,n,dd,x,y);//d*x+n*y=1-->(d*x)%n=1-->d*(x*b)%n==b
        x=(x*b%n+n)%n;
        if(f[x])
        {
            LL num=f[x];
            f.clear();//需要清空,不然会爆内存
            return c+i*m+(num==m?0:num);
        }
        d=(d*e)%n;
    }

    return -1;
}
int main()
{
    LL a,b,n;
    while(scanf("%I64d%I64d%I64d",&a,&n,&b)!=EOF)
    {
        if(b>=n)
        {
            printf("Orz,I can’t find D!\n");
            continue;
        }
        if(b==0)
        {
            printf("0\n");
            continue;
        }
        LL ans=0,c=0,d=1,t;
        while((t=gcd(a,n))!=1)
        {
            if(b%t){ans=-1;break;}
            c++;
            n=n/t;
            b=b/t;
            d=d*a/t%n;
            if(d==b){ans=c;break;}//特判下是否成立。
        }
        if(ans!=0)
        {
            if(ans==-1){printf("Orz,I can’t find D!\n");}
            else printf("%I64d\n",ans);
        }
        else
        {
            ans=log_mod(a,b,n,c,d);
            if(ans==-1)printf("Orz,I can’t find D!\n");
            else printf("%I64d\n",ans);
        }
    }
    return 0;
}
/*
    求解模方程a^x=b(mod n),n不为素数。拓展Baby Step Giant Step
    模板题。
    
    方法:
    初始d=1,c=0,i=0;
    1.令g=gcd(a,n),若g==1则执行下一步。否则由于a^x=k*n+b;(k为某一整数),则(a/g)*a^k=k*(n/g)+b/g,(b/g为整除,若不成立则无解)
令n=n/g,d=d*a/g,b=b/g,c++则d*a^(x-c)=b(mod n),接着重复1步骤。
    2.通过1步骤后,保证了a和d都与n互质,方程转换为d*a^(x-c)=b(mod n)。由于a和n互质,所以由欧拉定理a^phi(n)==1(mod n),(a,n互质)
可知,phi(n)<=n,a^0==1(mod n),所以构成循环,且循环节不大于n。从而推出如果存在解,则必定1<=x<n。(在此基础上我们就可以用
Baby Step Giant Step方法了)
    3.令m=ceil(sqrt(n)),则m*m>=n。用哈希表存储a^0,a^1,..,a^(m-1),接着判断1~m*m-1中是否存在解。
    4.为了减少时间,所以用哈希表缩减复杂度。分成m次遍历,每次遍历a^m长度。由于a和d都与n互质,所以gcd(d,n)=1,
所以用拓展的欧几里德定理求得d*x+n*y=gcd(d,n)=1,的一组整数解(x,y)。则d*x+n*y=1-->d*x%n=(d*x+n*y)%n=1-->d*(x*b)%n=((d*x)%n*b%n)%n=b。
所以若x*b在哈希表中存在,值为k,则a^k*d=b(mod n),答案就是ans=k+c+i*m。如果不存在,则令d=d*a^m,i++后遍历下一个a^m,直到遍历a^0到a^(m-1)还未找到,则说明不解并退出。

*/



5.中国剩余定理

互质情况

  1. //中国剩余定理,求得M%A=a,M%B=b,...中的M,其中A,B,C...互质
  2. int china(int a[])
  3. {
  4. int i,j,k,d,ans=0,x,y,M=1;
  5. for(i=0;i<n;i++)
  6. M=M*x[i];
  7. for(i=0;i<n;i++)
  8. {
  9. m=M/x[i];
  10. gcd(x[i],m,d,x,y);
  11. ans=(ans+y*m*a[i])%M;
  12. }
  13. return (ans+M)%M;
  14. }
//中国剩余定理,求得M%A=a,M%B=b,...中的M,其中A,B,C...互质  
int china(int a[])
{  
    int i,j,k,d,ans=0,x,y,M=1;
    for(i=0;i<n;i++)  
        M=M*x[i];
    for(i=0;i<n;i++)  
    {  
        m=M/x[i];  
        gcd(x[i],m,d,x,y);  
        ans=(ans+y*m*a[i])%M;  
    }  
    return (ans+M)%M;  
}

不互质的情况

  1. int China(int n)
  2. {
  3. int m1,r1,m2,r2,flag=0,i,d,x,y,c,t;
  4. scanf("%d%d",&m1,&r1);
  5. flag=0;
  6. for(i=1;i<n;i++)
  7. {
  8. scanf("%d%d",&m2,&r2);
  9. if(flag)continue;
  10. gcd(m1,m2,d,x,y);//d=gcd(m1,m2);x*m1+y*m2=d;
  11. c=r2-r1;
  12. if(c%d)//对于方程m1*x+m2*y=c,如果c不是d的倍数就无整数解
  13. {
  14. flag=1;
  15. continue;
  16. }
  17. t=m2/d;//对于方程m1x+m2y=c=r2-r1,若(x0,y0)是一组整数解,那么(x0+k*m2/d,y0-k*m1/d)也是一组整数解(k为任意整数)
  18. //其中x0=x*c/d,y0=x*c/d;
  19. x=(c/d*x%t+t)%t;//保证x0是正数,因为x+k*t是解,(x%t+t)%t也必定是正数解(必定存在某个k使得(x%t+t)%t=x+k*t)
  20. r1=m1*x+r1;//新求的r1就是前i组的解,Mi=m1*x+M(i-1)=r2-m2*y(m1为前i个m的最小公倍数);对m2取余时,余数为r2;
  21. //对以前的m取余时,Mi%m=m1*x%m+M(i-1)%m=M(i-1)%m=r
  22. m1=m1*m2/d;
  23. }
  24. if(flag)return -1;
  25. else return r1;
  26. }
int China(int n)  
{  
    int m1,r1,m2,r2,flag=0,i,d,x,y,c,t;  
    scanf("%d%d",&m1,&r1);  
    flag=0;  
    for(i=1;i<n;i++)  
    {  
        scanf("%d%d",&m2,&r2);  
        if(flag)continue;  
        gcd(m1,m2,d,x,y);//d=gcd(m1,m2);x*m1+y*m2=d;  
        c=r2-r1;  
        if(c%d)//对于方程m1*x+m2*y=c,如果c不是d的倍数就无整数解  
        {  
            flag=1;  
            continue;  
        }  
        t=m2/d;//对于方程m1x+m2y=c=r2-r1,若(x0,y0)是一组整数解,那么(x0+k*m2/d,y0-k*m1/d)也是一组整数解(k为任意整数)  
                //其中x0=x*c/d,y0=x*c/d;  
        x=(c/d*x%t+t)%t;//保证x0是正数,因为x+k*t是解,(x%t+t)%t也必定是正数解(必定存在某个k使得(x%t+t)%t=x+k*t)  
        r1=m1*x+r1;//新求的r1就是前i组的解,Mi=m1*x+M(i-1)=r2-m2*y(m1为前i个m的最小公倍数);对m2取余时,余数为r2;  
                    //对以前的m取余时,Mi%m=m1*x%m+M(i-1)%m=M(i-1)%m=r  
        m1=m1*m2/d;  
    }  
    if(flag)return -1; 
    else return r1;  
}  


6.素数

普通筛选法

  1. const int maxn=10000001;
  2. const int maxc=700000;
  3. int vis[maxn];//vis[i]=0时,i为素数或者1,否则为合数
  4. int prime[maxc];
  5. //筛素数
  6. void sieve(int n)
  7. {
  8. int m=(int)sqrt(n+0.5);//避免浮点误差
  9. memset(vis,0,sizeof(vis));
  10. for(int i=2;i<=m;i++)if(!vis[i])
  11. for(int j=i*i;j<=n;j+=i)vis[j]=1;
  12. }
  13. //生成素数表,存在prime数组中,返回素数个数
  14. int primes(int n)
  15. {
  16. sieve(n);
  17. int t=0;
  18. for(int i=2;i<=n;i++)if(!vis[i])
  19. prime[t++]=i;
  20. return t;
  21. }
const int maxn=10000001;
const int maxc=700000;
int vis[maxn];//vis[i]=0时,i为素数或者1,否则为合数
int prime[maxc];
//筛素数
void sieve(int n)
{
    int m=(int)sqrt(n+0.5);//避免浮点误差
    memset(vis,0,sizeof(vis));
    for(int i=2;i<=m;i++)if(!vis[i])
        for(int j=i*i;j<=n;j+=i)vis[j]=1;
}
//生成素数表,存在prime数组中,返回素数个数
int primes(int n)
{
    sieve(n);
    int t=0;
    for(int i=2;i<=n;i++)if(!vis[i])
        prime[t++]=i;
    return t;
}

线性筛法

:在O(n)时间复杂度内找出所有的素数

  1. void init()//预处理,找出所有1e7以内的素数,以减少查找1e14范围数的因子的时间
  2. { //现行筛素数的方法,时间复杂度为O(n)
  3. memset(check,false,sizeof(check));
  4. int i,j;
  5. tot=0;
  6. for(i=2;i<=1e7;i++)
  7. {
  8. if(!check[i])prime[tot++]=i;
  9. for(j=0;j<tot;j++)
  10. {
  11. if(i*prime[j]>1e7)break;
  12. check[i*prime[j]]=true;
  13. if(i%prime[j]==0)break;
  14. }
  15. }
  16. //printf("%d\n",tot);
  17. //for(i=0;i<20;i++)
  18. // printf("prime[%d]:%d\n",i,prime[i]);
  19. }
void init()//预处理,找出所有1e7以内的素数,以减少查找1e14范围数的因子的时间  
{           //现行筛素数的方法,时间复杂度为O(n)  
    memset(check,false,sizeof(check));  
    int i,j;  
    tot=0;  
    for(i=2;i<=1e7;i++)  
    {  
        if(!check[i])prime[tot++]=i;  
        for(j=0;j<tot;j++)  
        {  
            if(i*prime[j]>1e7)break;  
            check[i*prime[j]]=true;  
            if(i%prime[j]==0)break;  
        }  
    }  
    //printf("%d\n",tot);  
    //for(i=0;i<20;i++)  
    //    printf("prime[%d]:%d\n",i,prime[i]);  
} 




7.欧拉phi函数

,求不超过n且与n互质的正整数个数

  1. int euler_phi(int n)//求单个欧拉函数
  2. {
  3. int m=(int)sqrt(n+0.5);
  4. int i,ans=n;
  5. for(i=2;i<=m;i++)
  6. if(n%i==0)
  7. {
  8. ans=ans/i*(i-1);
  9. while(n%i==0)n/=i;
  10. }
  11. if(n>1)ans=ans/n*(n-1);
  12. return ans;
  13. }
int euler_phi(int n)//求单个欧拉函数
{
    int m=(int)sqrt(n+0.5);
    int i,ans=n;
    for(i=2;i<=m;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;
}

  1. int phi[maxn];
  2. void euler_phi()
  3. {
  4. int i,j,k;
  5. //欧拉函数,phi[i]表示不超过i的与i互质的整数个数
  6. for(i=2;i<maxn;i++)phi[i]=0;
  7. phi[1]=1;
  8. for(i=2;i<maxn;i++)
  9. if(!phi[i])
  10. for(j=i;j<=maxn;j+=i){
  11. if(!phi[j])phi[j]=j;
  12. phi[j]=phi[j]/i*(i-1);
  13. }
  14. }
int phi[maxn];  
void euler_phi()  
{  
    int i,j,k;  
    //欧拉函数,phi[i]表示不超过i的与i互质的整数个数  
    for(i=2;i<maxn;i++)phi[i]=0;  
    phi[1]=1;  
    for(i=2;i<maxn;i++)  
        if(!phi[i])  
        for(j=i;j<=maxn;j+=i){  
            if(!phi[j])phi[j]=j;  
            phi[j]=phi[j]/i*(i-1);  
        }  
} 

8.求三角形两边向量积

,面积S=1/2*|a×b|

  1. int cross(node a,node b,node c)//向量积
  2. {
  3. return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);//向量ac×bc
  4. }
int cross(node a,node b,node c)//向量积
{
    return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);//向量ac×bc
}

9.求凸包

,res数组存凸包上的点。

  1. int convex(int n)
  2. {
  3. sort(e,e+n,cmp);//根据x从小到大排序,x相等时根据y从小到大排序
  4. int m=0,i,j,k;
  5. //求得下凸包,逆时针
  6. for(i=0;i<n;i++)
  7. {
  8. while(m>1&&cross(res[m-1],e[i],res[m-2])<=0)m--;
  9. res[m++]=e[i];
  10. }
  11. k=m;
  12. //求得上凸包
  13. for(i=n-2;i>=0;i--)
  14. {
  15. while(m>k&&cross(res[m-1],e[i],res[m-2])<=0)m--;
  16. res[m++]=e[i];
  17. }
  18. if(n>1)m--;//起始点重复。
  19. return m;//返回凸包边界结点数。
  20. }
int convex(int n)
{
    sort(e,e+n,cmp);//根据x从小到大排序,x相等时根据y从小到大排序
    int m=0,i,j,k;
    //求得下凸包,逆时针
    for(i=0;i<n;i++)
    {
        while(m>1&&cross(res[m-1],e[i],res[m-2])<=0)m--;
        res[m++]=e[i];
    }
    k=m;
    //求得上凸包
    for(i=n-2;i>=0;i--)
    {
        while(m>k&&cross(res[m-1],e[i],res[m-2])<=0)m--;
        res[m++]=e[i];
    }
    if(n>1)m--;//起始点重复。
    return m;//返回凸包边界结点数。
}


10.三分法

  1. for(i=0;i<100;i++)
  2. {
  3. m1=l+(r-l)/3;
  4. m2=r-(r-l)/3;
  5. if(find(m1)<find(m2)) r=m2;
  6. else l=m1;
  7. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值