• 寒假集训日志(三)——数论


      今天听得简直要崩溃。。。没听懂啥。。。

      主要内容:

      1.欧几里得(稍微懂了点)

      2.中国剩余定理( 稍微懂了点)

      3.博弈( 看智商的玩意儿)

    (一)欧几里得算法(及其扩展算法)

      欧几里得定理就是gcd(辗转相除法)的原理(不懂,只会用)。

      扩展算法的运用大概就是用来解一个 ax + by = gcd( a, b )的不定方程。

      大致证明步骤:  将a 替换为b, 将b 替换为gcd(b, a%b),又gcd(a,b) = gcd( b, a%b),就可以化为一个等式巴拉巴拉的。然后算法实现的花就用递归:

      

    //第一种,之后修改所得的 x , y 就是一组特解,通解的话直接根据系数在特解的基础上加减就好了
    void exGcd ( ll a, ll b, ll &x , ll &y){
        if( b== 0) {
            x =1 ; y = 0;
            return ;
        }
        else{
        exGcd ( b, a% b, x, y);
        ll t = x;
        x = y;
        y = t - a/b* y;
        }
    }
    //第二种,简短,d最后得到的是 a, b的最大公约数 。 另外,注意这段代码 x, y需要交换位置的部分
    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);
      }
    }

    今天真正会做的也就3道。。。两道是这个算法的同类题,就放一道把。。。

    A - 青蛙的约会
    Time Limit:1000MS     Memory Limit:10000KB     64bit IO Format:%I64d & %I64u
    Submit Status

    Description

    两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是 它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下 去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只 青蛙是否能够碰面,会在什么时候碰面。
    我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了 一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬 度线总长L米。现在要你求出它们跳了几次以后才会碰面。

    Input

    输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。

    Output

    输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"

    Sample Input

    1 2 3 4 5

    Sample Output

    4
    我的代码:
    //这个题目要注意一下long long
    #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> typedef long long ll; using namespace std; ll gcd (ll a ,ll b){ return b==0?a: gcd( b, a%b); } void exGcd ( ll a, ll b, ll &x , ll &y){ if( b== 0) { x =1 ; y = 0; return ; } else{ exGcd ( b, a% b, x, y); ll t = x; x = y; y = t - a/b* y; } } int main(){ ll x, y, m, n, L, k, t; cin>>x>>y>>m>>n>>L; ll a = n - m; ll b = L; ll c = x - y; ll d =gcd ( a,b); if( c % d !=0 ){ cout<<"Impossible"<<endl; return 0; } a /=d; b /= d; c /= d; exGcd ( a, b , t, k ); t = c* t%b; if( t < 0 ) t+=b; //printf("%I64d ", t); cout<< t<<endl; return 0; }

    (二) 中国剩余定理:

      就是解决一个似乎是叫韩信点兵的问题。(以下转自http://yzmduncan.iteye.com/blog/1323599/)

      互质版的很好懂,就是直接一公式就出来了。

    MOD M

      非互质版的比较麻烦,代码也有点乱,就是采取合并的方法,暂时没弄懂,需好好体会。

    中国剩余定理

         中国剩余定理是中国古代求解一次同余方程组的方法,是数论中的一个重要定理。

         设m1,m2,m3,...,mk是两两互素的正整数,即gcd(mi,mj)=1,i!=j,i,j=1,2,3,...,k.

    则同余方程组:

    x = a1 (mod n1)

    x = a2 (mod n2)

    ...

    x = ak (mod nk)

    模[n1,n2,...nk]有唯一解,即在[n1,n2,...,nk]的意义下,存在唯一的x,满足:

    x = ai mod [n1,n2,...,nk], i=1,2,3,...,k。

    解可以写为这种形式:

    x = sigma(ai* mi*mi') mod(N)

          其中N=n1*n2*...*nk,mi=N/ni,mi'为mi在模ni乘法下的逆元。

    中国剩余定理非互质版

        中国剩余定理求解同余方程要求模数两两互质,在非互质的时候其实也可以计算,这里采用的是合并方程的思想。下面是详细推导。

    互质版:

        #include <iostream>  
        #include <cstdio>  
        #include <cstring>  
        using namespace std;  
        typedef __int64 int64;  
        int64 a[15],b[15];  
          
        int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y)  
        {  
            if(b==0)  
            {  
                x=1,y=0;  
                return a;  
            }  
            int64 d = Extend_Euclid(b,a%b,x,y);  
            int64 t = x;  
            x = y;  
            y = t - a/b*y;  
            return d;  
        }  
        //求解模线性方程组x=ai(mod ni)  
        int64 China_Reminder(int len, int64* a, int64* n)  
        {  
            int i;  
            int64 N = 1;  
            int64 result = 0;  
            for(i = 0; i < len; i++)  
                N = N*n[i];  
            for(i = 0; i < len; i++)  
            {  
                int64 m = N/n[i];  
                int64 x,y;  
                Extend_Euclid(m,n[i],x,y);  
                x = (x%n[i]+n[i])%n[i];  
                result = (result + m*a[i]*x%N)%N;  
            }  
            return result;  
        }  
          
        int main()  
        {  
            int n;  
            while(scanf("%d",&n)!=EOF)  
            {  
                for(int i = 0; i < n; i++)  
                    scanf("%I64d %I64d",&a[i],&b[i]);  
                printf("%I64d
    ",China_Reminder(n,b,a));  
            }  
            return 0;  
        }  

    非互质版:

        /** 
        中国剩余定理(不互质) 
        */  
        #include <iostream>  
        #include <cstdio>  
        #include <cstring>  
        using namespace std;  
        typedef __int64 int64;  
        int64 Mod;  
          
        int64 gcd(int64 a, int64 b)  
        {  
            if(b==0)  
                return a;  
            return gcd(b,a%b);  
        }  
          
        int64 Extend_Euclid(int64 a, int64 b, int64&x, int64& y)  
        {  
            if(b==0)  
            {  
                x=1,y=0;  
                return a;  
            }  
            int64 d = Extend_Euclid(b,a%b,x,y);  
            int64 t = x;  
            x = y;  
            y = t - a/b*y;  
            return d;  
        }  
          
        //a在模n乘法下的逆元,没有则返回-1  
        int64 inv(int64 a, int64 n)  
        {  
            int64 x,y;  
            int64 t = Extend_Euclid(a,n,x,y);  
            if(t != 1)  
                return -1;  
            return (x%n+n)%n;  
        }  
          
        //将两个方程合并为一个  
        bool merge(int64 a1, int64 n1, int64 a2, int64 n2, int64& a3, int64& n3)  
        {  
            int64 d = gcd(n1,n2);  
            int64 c = a2-a1;  
            if(c%d)  
                return false;  
            c = (c%n2+n2)%n2;  
            c /= d;  
            n1 /= d;  
            n2 /= d;  
            c *= inv(n1,n2);  
            c %= n2;  
            c *= n1*d;  
            c += a1;  
            n3 = n1*n2*d;  
            a3 = (c%n3+n3)%n3;  
            return true;  
        }  
          
        //求模线性方程组x=ai(mod ni),ni可以不互质  
        int64 China_Reminder2(int len, int64* a, int64* n)  
        {  
            int64 a1=a[0],n1=n[0];  
            int64 a2,n2;  
            for(int i = 1; i < len; i++)  
            {  
                int64 aa,nn;  
                a2 = a[i],n2=n[i];  
                if(!merge(a1,n1,a2,n2,aa,nn))  
                    return -1;  
                a1 = aa;  
                n1 = nn;  
            }  
            Mod = n1;  
            return (a1%n1+n1)%n1;  
        }  
        int64 a[1000],b[1000];  
        int main()  
        {  
            int i;  
            int k;  
            while(scanf("%d",&k)!=EOF)  
            {  
                for(i = 0; i < k; i++)  
                    scanf("%I64d %I64d",&a[i],&b[i]);  
                printf("%I64d
    ",China_Reminder2(k,b,a));  
            }  
            return 0;  
        }  

    题目就不给出了,基本一眼就可以看出来,也很难有什么改变。

    (三)博弈论

      此类题变幻无穷。。。及其考智商,只是任何时候都别忘了dp。。

      另外,打表的方法一定要学会。

       直接加上两句话就行了:

      freopen("input.txt", "r", stdin);   //将文件中的数据输入

      freopen("output.txt", "w", stdout);  //将程序中的输出输出到文件中

  • 相关阅读:
    linux设备驱动第五篇:驱动中的并发与竟态
    chromium浏览器开发系列第二篇:如何编译最新chromium源码
    你所不知道的html5与html中的那些事(二)
    vim 高级使用技巧第二篇
    FFMPEG高级编程第一篇:环境搭建及编译
    android apk 防止反编译技术第一篇-加壳技术
    你所不知道的html5与html中的那些事(一)
    交通视频
    Git命令----放弃本地修改使用服务器上的代码
    IE10(去掉文本框的X)
  • 原文地址:https://www.cnblogs.com/topW2W/p/5156283.html
Copyright © 2020-2023  润新知