zoukankan      html  css  js  c++  java
  • 关于数论的整理

    一丶扩展欧几里得算法

    欧几里得算法是 辗转相除法?一种求两个数的最大公约数的算法:gcd(a,b)=gcd(b,a%b)

    在这里不详细展开讲,直接给出代码

    1 int gcd(int a,int b){
    2     return b==0 ? a : gcd(b,a%b);
    3 }
    欧几里得

    那么来说扩展欧几里得:

    找出一对数对(x,y),是的ax+by=gcd(a,b)

    ∵gcd(a,b)=gcd(b,a%b) 欧几里得定理

    ∴ax1 + by1 = bx2+ (a%b)y2

    ∵在整除意义下,a%b=a-(a/b)*b

    ∴ax1 + by1 = bx2 + [a-(a/b)*b] y2

    ∴ax1 + by1 = bx2 + a* y2 - b*(a/b)*y2  右边展开

    ∴ax1 + by1 = ay2 + b*[x2 - (a/b) *y2] 右边合并同类项

    根据恒等定理得,x1 = y2 ,y1 = x2 - (a/b)y2

    所以问题就变得简单了,使用递归求解

    边界条件:gcd(a,0)= 1 * a - 0 * 0 = a

    void gcd(int a,int b,int &d,int &x,int &y)
    {
        if(b==0){d=a;a=1;y=0;}
        else 
        {
            gcd(b,a%b,d,y,x);
            y-=x*(a/b);
        }
    }

    (若x为方程解,则x≡y(modn)的其他整数y也是方程解)

    二、同余方程

    定理1  若gcd(a,b)=d,则一定能找出一组(x,y),满足ax+by=d

    证明:叫裴蜀定理?

    设存在x,y使ax+by=d,d是ax+by取值中的最小正整数,d≠1.再设am+bn=e,则e≥d .若d不整除e,对e做带余除法.必定存在p,r使e=pd+r

    所以d一定能写成ax+by的形式。

    a = b*q1 + r1

    b=  r1*q2+ r2

    r1= r2*q3+r3

    ……

    r1=q-b*q1

    r2=b-r1*q2

    r3=r1-r2*q3

    ……

    定理2  若gcd(a,b)=1,则方程ax≡c(mod b) 在[0,b-1]上有唯一解

    定理3  若gcd(a,b)=d,则方程ax≡c(mod b) 在[0,b/d-1]上有唯一解

    来看这么一道题:同余方程

    noip 2012 提高组 同余方程 http://cogs.pro/cogs/problem/problem.php?pid=1265

    axb(modn)

    可以看出 a-b是n的整数倍

    那么设这个倍数为y

    则ax-b=n*y

    移项得ax-ny=b(不定方程 a,n,b为已知量,x,y为未知量)

    给出代码:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 using namespace std;
     5 long long modd(long long m,long long n,long long &x,long long &y) 
     6 {
     7     if(!n) 
     8     {
     9         x=1;
    10         y=0;
    11         return m;
    12     } 
    13     else 
    14     {
    15         long long r=modd(n,m%n,x,y);
    16         long long t=x;
    17         x=y;
    18         y=t-m/n*y;
    19         return r;
    20     }
    21 }
    22 
    23 int main() {
    24     long long n,m,x,y;
    25     cin>>m>>n;
    26     long long  gcd=modd(m,n,x,y);
    27     while(x<0)x+=n/gcd;
    28     cout<<x;
    29     return 0;
    30     fclose(stdin);
    31     fclose(stdout);
    32 }
    同余方程

    三丶筛法求素数

    用筛法构造素数表

    思想:对不超过n的非负整数a,删除2a,3a,4a..........,当处理完所数后没有被筛去的就是素数。

    用数组 vis[i]表示,若vis[i]=1,表示已被筛去,i不是素数,=0表i是素数。

    memset(vis,0,sizeof(vis));
    vis[1]=1;
    for(int i=2;i<=n;i++)
      for(int j=i*2;j<=n;j+=i) {vis[j]=1;}

    时间复杂度为O(nlogn)

    改进:可以把a限定为素数;在两个for中间加一个if(!vis[i])

    外层循环循环到sqrt(n)+0.5就好,因为n的倍数都能用 根n 得到 +0.5防止向下取整数

    内层循环中的j=i*2可以改成i*i,因为i*2已经在i=2时筛掉了

    下边是代码

    memset(vis,0,sizeof(vis));
    vis[1]=1;
    for(int i=2;i<=sqrt(n)+0.5;i++)
      if(!vis[i])
      for(int j=i*i;j<=n;j+=i)
      {
          vis[j]=1;
      }
     1 #include <iostream>
     2 using namespace std;
     3 
     4 const int MD = 1e9 + 7;
     5 
     6 int pow(int n, int m) 
     7 {
     8     if(m == 0) return 1;
     9     
    10     int t = pow(n, m / 2);
    11     
    12     t = 1LL * t * t % MD;
    13     if(m&1) t = 1LL * t * n % MD;
    14     
    15     return t;
    16 }
    17 
    18 int pow2(int n, int m)
    19 {
    20     int r(1), s(n);
    21     for(; m; m>>=1, s = 1LL*s*s%MD) 
    22         if(m&1) r = 1LL*r*s%MD;
    23     return r;
    24 }
    25 
    26 int main()
    27 {
    28     int n, m;
    29     cin >> n >> m;
    30     cout << pow(n, m) << endl;
    31     cout << pow2(n, m) << endl;
    32     return 0;
    33 }
    pow

    模板:

    三丶 斐波那契数列

    通项公式:

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstdio>
     4 using namespace std;
     5 int main()
     6 {
     7     int n;
     8     scanf("%d",&n);
     9     double x=sqrt(5.0);
    10     int ans=( pow ( ( ( 1+x ) / 2.0 ) , n ) / x - pow ( ( ( 1 - x ) / 2.0 ), n ) /x);
    11     printf("%d",ans);
    12     return 0;
    13 }
    通项公式

    四丶求二元一次不定方程

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 using namespace std;
     5 int x,y;
     6 int gcd(int a,int b)
     7 {
     8     if(b==0)
     9     return a;
    10     else 
    11     return gcd(b,a%b);
    12 }
    13 int exgcd(int a,int b,int &x,int &y)
    14 {
    15     if(b==0)
    16     {
    17         x=1;
    18         y=0;
    19         return a;
    20     }
    21     int r=exgcd(b,a%b,x,y);
    22     int temp=x;
    23     x=y;
    24     y=temp-(a/b)*y;
    25     return r;
    26 }
    27 int main()
    28 {
    29     int a,b,c;
    30     scanf("%d%d%d",&a,&b,&c);
    31     int y=gcd(a,b);
    32     if(c%y!=0)
    33     {
    34         printf("-1");
    35         return 0;
    36     }
    37     else exgcd(a,b,x,y);
    38     while(x<0)
    39     {
    40         x=x+b;
    41         y=y+b;
    42     }
    43     x*=c;
    44     printf("%d %d",x,y);
    45     return 0;
    46 }
    二元一次不定方程

    五丶快速幂

    下面是从别人博客找的,自己就懒得写了http:快速幂

    快速幂这个东西比较好理解,但实现起来到不老好办,记了几次老是忘,今天把它系统的总结一下防止忘记。

      首先,快速幂的目的就是做到快速求幂,假设我们要求a^b,按照朴素算法就是把a连乘b次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(logn),快了好多好多。它的原理如下:

      假设我们要求a^b,那么其实b是可以拆成二进制的,该二进制数第i位的权为2^(i-1),例如当b==11时

    a^11=a^(2^0+2^1+2  11的二进制是1011,11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1,因此,我们将a¹¹转化为算 a^(2^0)*a^(2^1)*a^(2^3) 
    ,看出来快的多了吧原来算11次,现在算三次,但是这三项貌似不好求的样子....不急,下面会有详细解释。
      由于是二进制,很自然地想到用位运算这个强大的工具: &  和 >>  
      &运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶x&1==0为偶,x&1==1为奇。
      >>运算比较单纯,二进制去掉最后一位,不多说了,先放代码再解释
     1 int poww(int a,int b){
     2     int ans=1,base=a;
     3     while(b!=0){
     4         if(b&1!=0)
     5           ans*=base;
     6         base*=base;
     7         b>>=1;
     8   }
     9     return ans;
    10 }

    代码很短,死记也可行,但最好还是理解一下吧,其实也很好理解,以b==11为例,b=>1011,二进制从右向左算,但乘出来的顺序是 a^(2^0)*a^(2^1)*a^(2^3),是从左向右的。我们不断的让base*=base目的即是累乘,以便随时对ans做出贡献。

      其中要理解base*=base这一步,看:::base*base==base^2,下一步再乘,就是base^2*base^2==base^4,然后同理  base^4*base4=base^8,,,,,see?是不是做到了base-->base^2-->base^4-->base^8-->base^16-->base^32.......指数正是 2^i 啊,再看上  面的例子,a¹¹= a^(2^0)*a^(2^1)*a^(2^3),这三项是不是完美解决了,,嗯,快速幂就是这样。

      顺便啰嗦一句,由于指数函数是爆炸增长的函数,所以很有可能会爆掉int的范围,根据题意决定是用 long long啊还是unsigned int啊还是mod某个数啊自己看着办。

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstdio> 
     4 using namespace std;
     5 int fastpow(int a,int b)
     6 {
     7     int r=1;
     8     int step=a;
     9     while(b!=0)
    10     {
    11         if(b%2!=0)
    12         r=r*step;
    13         step=step*step;
    14         b=b/2;
    15     }
    16     return r;
    17 }
    18 int main()
    19 {
    20     int a,b;
    21     scanf("%d%d",&a,&b);
    22     int ans=fastpow(a,b);
    23     printf("%d",ans);
    24     return 0;
    25 }
    快速幂

    六丶排列数

    (1)基本排列

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int f(int n)
     6 {
     7     if(n==0)return 1;
     8     else return n*f(n-1);
     9 }
    10 int main()
    11 {
    12     int a,b;
    13     scanf("%d%d",&a,&b);
    14     printf("%d",f(a)/f(a-b));
    15     return 0;
    16 }
    基本排列

    (2)全排列

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int f(int n)
     6 {
     7     if(n==0)return 1;
     8     else return n*f(n-1);
     9 }
    10 int main()
    11 {
    12     int a,b;
    13     scanf("%d%d",&a,&b);
    14     printf("%d",f(a));
    15     return 0;
    16 }
    全排列

    (3)圆排列

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 int f(int n)
     6 {
     7     if(n==0)return 1;
     8     else return n*f(n-1);
     9 }
    10 int main()
    11 {
    12     int a,b;
    13     scanf("%d%d",&a,&b);
    14     printf("%d",f(a)/(f(a-b)*b));
    15     return 0;
    16 }
    圆排列

    (4)项链排列

     1 #include<iostream>
     2 #include<cmath>
     3 #include<cstdio>
     4 using namespace std;
     5 int f(int n)
     6 {
     7     if(n==0)return 1;
     8     else return n*f(n-1);
     9 }
    10 int main()
    11 {
    12     int a;
    13     scanf("%d",&a);
    14     if(a==1||a==2)printf("1");
    15     else printf("%d",f(a-1)/2);
    16     return 0;
    17 }
    项链排列

    七丶组合数

    求组合数

     

  • 相关阅读:
    Charles:rewrite重写功能
    Vue中provide和inject 用法
    vue中install方法
    vue自定义组件(通过Vue.use()来使用)即install的使用
    Eelectron 中的remote模块
    理解Vue中的Render渲染函数
    Vue.js中this.$nextTick()的使用
    postman请求本地接口Error: connect ECONNREFUSED 127.0.0.1:8083
    1016 Phone Bills (25 分)
    CF842E Nikita and game
  • 原文地址:https://www.cnblogs.com/sssy/p/6740735.html
Copyright © 2011-2022 走看看