zoukankan      html  css  js  c++  java
  • 数论 + 扩展欧几里得

     The equation 

    Problem's Link


     

    Mean: 

    给你7个数,a,b,c,x1,x2,y1,y2.求满足a*x+b*y=-c的解x满足x1<=x<=x2,y满足y1<=y<=y2.求满足条件的解的个数.

    analyse:

    做法是扩展欧几里德.

    1.首先是欧几里德算法,欧几里德算法是用于求任意两个数的最大公约数(gcd(a,b)),

    这个方法基于一个定理,gcd(a,b)=gcd(b,a % b)(a>b),%表示取模.

    我们来证明上述定理,因为a>b,所以我们可以将a表示成a=kb+r,

    假设gcd(a,b)=d,也就是两个数的最大公约数为d.

    那么d|a,d|b,这是显然的.

    又因为r=a-kb,所以d|r.

    而r的值就像当于a % b,

    所以gcd(a,b)=gcd(b,a%b).

    2、欧几里德扩展算法。根据贝祖定理,如果gcd(a,b)=d,那么一定存在整数x,y使得a*x+b*y=gcd(a,b)=d.并且a和b的线性和都为d的整数倍.

    欧几里德扩展算法就是用来求出这个线性方程的一个解的。注意,只是其中一个解.

    首先设两个数aa=b,bb=a%b=a-a/b*b;

    因为gcd(a,b)=gcd(b,a%b)=gcd(aa,bb).

    所以aa*xx+bb*yy=gcd(aa,bb)=gcd(a,b)

    我们用a和b来表示aa,bb.

    所以b*xx+(a-a/b*b)yy=gcd(a,b)

    a*yy+b*(xx-a/b*yy)=gcd(a,b).

    对照不定方程a*x+b*y=gcd(a,b)

    我们可以得到该不定方程的解为x=yy,y=xx-a/b*yy.

    当我们递归上述操作时会得到b=0的情况,此时式子相当与a*xx=a,

    所以此时xx=1,yy=0;以此做返回值.

    3、对于a*x+b*y=n这个不定方程的求解。
    根据贝祖定理,我们知道a*x+b*y这个不定方程的值一定是gcd(a,b)的整数倍。
    那么如果n不是gcd(a,b)的倍数,该不定方程一定没有整数解。
    我们先求解出gcd(a,b).
    方程两边同时除以gcd(a,b).我们假设aa=a/gcd(a,b),bb=b/gcd(a,b),nn=n/gcd(a,b)
    所以方程两边同时除以gcd(a,b)后,
    可以得到一个方程aa*x+bb*y=nn.
    并且该方程aa*x+bb*y=nn的解x,y就是a*x+b*y=n的解。
    我们转化成这个方程有什么用处呢?
    用处就在于gcd(aa,bb)=1.
    我们只要求解出aa*x+bb*y=1的其中一个解,设这两个解为x0,y0.
    那么aa*x+bb*y=nn的其中一个解解就是x0*nn,y0*nn.
    接着,a*x+b*y=n的其中一个解解也就是x0*nn,y0*nn.

    很显然,这道题目要我们求解的个数,一个解是不够的。
    我们继续看a*x+b*y=n这个式子,它的一个解为x0*nn,y0*nn.我们尝试代入,得到
    a*(x0*nn)+b*(y0*nn)=n.
    我们会发现
    a*(x0*nn+1*b)+b*(y0*nn-1*a)=n
    a*(x0*nn-1*b)+b*(y0*nn+1*a)=n.
    继续推广
    a*(x0*nn+k*b)+b*(y0*nn-k*a)=n (k属于整数)
    nn=n/gcd(a,b).
    那么一个结论出来了
    『x=x0*nn+k*b
    y=y0*nn-k*a
    k属于整数
    nn=n/gcd(a,b)
    x0,y0,为a/gcd(a,b)*x+b/gcd(a,b)*y=1的一个解』
    为原不定方程a*x+b*y=n的所有解。

    4、关于sgu106的求解。
    sgu106与a*x+b*y=n这个不定方程求解这两个问题的不同之处就在于。
    sgu106的解有取值范围。
    我们把x0*nn,y0*nn看成两个常数xz,yz.
    那么原方程解就变成两个一次函数
    x=k*b+xz(1)
    y=-k*a+yz(2)
    sgu106这道题就转化成为了求k所能够取到的整数个数
    首先,弄清出已知与未知。
    我们已知xz,yz,也已知方程左边的x和y的取值范围为x1到x2,y1到y2,也已知a和b
    我们只需要求出k即可。
    对于方程(1),我们带入x1,x2当作x的值,那么可以求出k1,k2(k1<k2)
    同理,带入(2),我们可以求出k3,k4.(k3<k4)
    那么我们最后的答案ans=min(k2,k4)-max(k1,k3)+1即可。

    Time complexity: O(N)

     

    view code

    /**
    * -----------------------------------------------------------------
    * Copyright (c) 2016 crazyacking.All rights reserved.
    * -----------------------------------------------------------------
    *       Author: crazyacking
    *       Date  : 2016-01-08-10.51
    */
    #include <queue>
    #include <cstdio>
    #include <set>
    #include <string>
    #include <stack>
    #include <cmath>
    #include <climits>
    #include <map>
    #include <cstdlib>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    typedef long long(LL);
    typedef unsigned long long(ULL);
    const double eps(1e-8);

    long long a, b, c;
    long long x1, x2, y1, y2;
    long long x, y;

    long long exgcd(long long a, long long b)
    {
       if(b == 0)
       {
           x = 1;
           y = 0;
           return a;
       }
       else
       {
           long long t = exgcd(b, a%b), s = x;
           x = y;
           y = s-(a/b)*y;
           return t;
       }
    }

    long long solve()
    {
       scanf("%lld %lld %lld", &a, &b, &c);
       scanf("%lld %lld %lld %lld", &x1, &x2, &y1, &y2);
       c = -c;

       if(a == 0 && b == 0 && c != 0)
       {
           return 0;
       }
       if(a == 0 && b == 0 && c == 0)
       {
           return (x2-x1+1)*(y2-y1+1);
       }

       if(a == 0)
       {
           long long sign = 0;
           if(y1 <= c/b && y2 >= c/b && c%b == 0)
           {
               sign = 1;
           }
           return (x2-x1+1)*sign;
       }
       if(b == 0)
       {
           long long sign = 0;
           if(x1 <= c/a && x2 >= c/a && c%a == 0)
           {
               sign = 1;
           }
           return (y2-y1+1)*sign;
       }

       long long gcd = exgcd(a, b);
       long long ans = 0;

       if(c%gcd != 0)
       {
           return 0;
       }

       /* 求交集 */
       x = x*(c/gcd);
       y = y*(c/gcd);
       // 减小步长
       a /= gcd;
       b /= gcd;
       long long lx = (x1-x>0&&(x1-x)%b!=0)?
                      ((x1-x)/b+1):
                      ((x1-x)/b);
       long long rx = (x2-x<0&&(x2-x)%b!=0)?
                      ((x2-x)/b-1):
                      ((x2-x)/b);
       long long ly = (y-y2>0&&(y-y2)%a!=0)?
                      ((y-y2)/a+1):
                      ((y-y2)/a);
       long long ry = (y-y1<0&&(y-y1)%a!=0)?
                      ((y-y1)/a-1):
                      ((y-y1)/a);
       if(lx > rx)
       {
           swap(lx, rx);
       }
       if(ly > ry)
       {
           swap(ly, ry);
       }
       if(rx >= ly && ry >= lx)
       {
           ans = min(rx,ry)-max(lx,ly)+1;
       }
       return ans;
    }

    int main(int argc, char **argv)
    {
       printf("%lld", solve());
       return 0;
    }
  • 相关阅读:
    Oracle Index 索引监控
    Oracle Job
    Oracle 数据类型
    Greenplum 的发展历史
    Mongodb账户管理
    MongoDB 备份与恢复
    MySQL 查看用户授予的权限
    Linux 不同方法查看进程消耗CPU IO 等
    Oracle 体系结构图
    Oracle 后台进程(六)PMON进程
  • 原文地址:https://www.cnblogs.com/crazyacking/p/5112638.html
Copyright © 2011-2022 走看看