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


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

      主要内容:

      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);  //将程序中的输出输出到文件中

  • 相关阅读:
    Jzoj4822 完美标号
    Jzoj4822 完美标号
    Jzoj4792 整除
    Jzoj4792 整除
    Educational Codeforces Round 79 A. New Year Garland
    Good Bye 2019 C. Make Good
    ?Good Bye 2019 B. Interesting Subarray
    Good Bye 2019 A. Card Game
    力扣算法题—088扰乱字符串【二叉树】
    力扣算法题—086分隔链表
  • 原文地址:https://www.cnblogs.com/topW2W/p/5156283.html
Copyright © 2020-2023  润新知