题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=106

 

题意:求ax + by + c = 0在[x1, x2], [y1, y2]区间内有多少组解?

解析:

令c
= -c有ax + by = c,可用扩展欧几里德解方程解出特解

当然要先考虑a
= 0, b = 0, c = 0的情况进行特判

例如:a = 0, b = 1, c = 3,x∈[x1,
x2], y∈[3, 4]

即可得知有方程有x2-x1+1个解,因为x可以区间内任意,且y=3这个解在区间内,其他情况同理了

②然后就是关键了,用的是扩展欧几里德通解式:(设一整数k,x0为x的特解)

1、x1 <= x0+k*b <= x2

2、y0 = (c - a*x0) / b

3、y1 <= y0+k'*b <= y2

解出k和k'的范围[s, e]!

注意了,例如解x1 <= x0+k*b:

当b<0时两边同时除以b,<=要变成>=号

--->k <= (x1-x0) / b

然后最关键就是除不尽应该向什么方向舍入?

区间[s, e]的舍入方向:

正数s, e的情况:

可见s = (x1-x0)/b还要+1,e直接除即可

负数s, e的情况:

可见e = (x1-x0)/b还要-1,s直接除即可

③最后得到x的k的范围[s1, e1], y的k'的范围[s2, e2]

因为x增加必然导致y减小,所以有:



举个例子,如图所示:令-e2作为s2,令-s2作为e2,答案为[-e2, e1]

 

所以答案ans = min (e1, e2') - max (s1, s2') + 1;

#include <iostream>
#include <stdio.h>
#include <string>
#include <cstring>
#include <stdlib.h>
#include <map>
#include <algorithm>
#include <math.h>
using namespace std;
#define M 1005
#define LL long long
LL max(LL a,LL b){
return a>b?a:b;
}
LL min(LL a,LL b){
return a<b?a:b;
}
LL gcd (LL a, LL b)
{
return b ? gcd (b, a%b) : a;
} void Egcd (LL a, LL b, LL &x, LL &y)
{
if (b == 0)
{
x = 1, y = 0;
return ;
}
LL tp;
Egcd (b, a%b, x, y);
tp = x;
x = y;
y = tp - a/b*y;
} LL cal (LL f, LL n, int key) //处理舍入问题
{
if (f % n == 0) return f/n;
if (key == 0)
{
if (f * n < 0) return f/n;
return f/n+1;
}
if (f * n < 0) return f/n-1;
return f/n;
} LL a, b, c;
LL solve (LL &x1, LL &x2, LL &y1, LL &y2)
{
LL d, x, y, s1, e1, s2, e2;
c = -c;
/***********************特判***********************/
if (a == 0 && b == 0 && c == 0)
return (x2-x1+1) * (y2-y1+1);
if (a == 0 && b == 0) return 0;
if (a == 0)
{
if (c % b != 0) return 0;
y = c / b;
if (y >= y1 && y <= y2) return x2 - x1 + 1;
return 0;
}
if (b == 0)
{
if (c % a != 0) return 0;
x = c / a;
if (x >= x1 && x <= x2) return y2 - y1 + 1;
return 0;
}
/***********************特判***********************/
d = gcd (a, b);
if (c % d != 0) return 0;
a /= d, b /= d, c /= d;
Egcd (a, b, x, y); x *= c;
x = (x % b + b) % b; //x的特解
s1 = cal (x1-x, b, b<0); //s1
e1 = cal (x2-x, b, b>0); //e1 y = (c - a*x) / b; //有了x,求对应的y
e2 = -cal (y1-y, a, a<0); //e2' = -s2
s2 = -cal (y2-y, a, a>0); //s2' = -e2 if (b < 0) s1 ^= e1, e1 ^= s1, s1 ^= e1; //b为负数,变号导致区间头尾互换
if (a < 0) s2 ^= e2, e2 ^= s2, s2 ^= e2; //同理 LL ans = min (e1, e2) - max (s1, s2) + 1;
if (ans < 0) ans = 0; return ans;
} int main()
{
LL x1, x2, y1, y2;
while (~scanf ("%lld%lld%lld", &a, &b, &c))
{
scanf ("%lld%lld%lld%lld", &x1, &x2, &y1, &y2);
printf ("%lld\n", solve (x1, x2, y1, y2));
}
return 0;
}

这里讲下:

扩展欧几里德算法是用来在已知a, b求解一组p,q使得p * a+q * b = Gcd(p, q) (解一定存在,根据数论中的相关定理)。扩展欧几里德常用在求解模线性方程及方程组中。下面是一个使用C++的实现:

  int exGcd(int a, int b, int &x, int &y)

  {

  if(b == 0)

  {

  x = 1;

  y = 0;

  return a;

  }

  int r = exGcd(b, a % b, x, y);

  int t = x;

  x = y;

  y = t - a / b * y;

  return r;

  }

  把这个实现和Gcd的递归实现相比,发现多了下面的x,y赋值过程,这就是扩展欧几里德算法的精髓。

  可以这样思考:

  对于a' = b, b' = a % b 而言,我们求得 x, y使得 a'x + b'y = Gcd(a', b')

  由于b' = a % b = a - a / b * b (注:这里的/是程序设计语言中的除法)

  那么可以得到:

  a'x + b'y = Gcd(a', b') ===>

  bx + (a - a / b * b)y = Gcd(a', b') = Gcd(a, b) ===>

  ay +b(x - a / b*y) = Gcd(a, b)

  因此对于a和b而言,他们的相对应的p,q分别是 y和(x-a/b*y)

  补充:关于使用扩展欧几里德算法解决不定方程的办法

  对于不定整数方程pa+qb=c,若 c mod Gcd(p, q)=0,则该方程存在整数解,否则不存在整数解。

  上面已经列出找一个整数解的方法,在找到p * a+q * b = Gcd(p, q)的一组解p0,q0后,p * a+q * b = Gcd(p, q)的其他整数解满足:

  p = p0 + b/Gcd(p, q) * t 

  q = q0 - a/Gcd(p, q) * t(其中t为任意整数)

  至于pa+qb=c的整数解,只需将p * a+q * b = Gcd(p, q)的每个解乘上 c/Gcd(p, q) 即可。

转:
首先扩展欧几里德主要是用来与求解线性方程相关的问题,所以我们从一个线性方程开始分析。现在假设这个线性方程为a*x+b*y=m,如果这个线性方程有解,那么一定有gcd(a,b)
| m,即a,b的最大公约数能够整除m(m%gcd(a,b)==0)。证明很简单,由于a%gcd(a,b)==b%gcd(a,b)==0,所以a*x+b*y肯定能够整除gcd(a,b),如果线性方程成立,那么就可以用m代替a*x+b*y,从而得到上面的结论,利用上面的结论就可以用来判断一个线性方程是否有解。
 
      那么在a*x+b*y=m这个线性方程成立的情况下,如何来求解x和y呢?
 
      1.令a1=a/gcd(a,b),b1=b/gcd(a,b),m1=m/gcd(a,b)。如果我们能够首先求出满足a*x1+b*y1=gcd(a,b)这个方程的x1和y1,那么x=x1*m1,y=y1*m1就可以求出来了。由欧几里德算法gcd(a,b)=gcd(b,a%b),所以a*x1+b*y1=gcd(a,b)=gcd(b,a%b)=b*x2+(a%b)*y2,现在只要做一些变形就可以得到扩展欧几里德算法中的用到的式子了。令k=a/b(商),r=a%b(余数),那么a=k*b+r。所以r=a-k*b,带入上式,得到a*x1+b*y1=b*x2+(a-(a/b)*b)y2=a*y2+b*(x2-(a/b)*y2)
=> x1=y2,y1=x2-(a/b)*y2。有了这两个式子我们就知道了在用欧几里德求最大公约数的时候,相应的参数x,y的变化。现在再回过头来看一下扩展欧几里德算法的代码就很好理解了,实际上扩展欧几里德就是在求a和b的最大公约数的同时,也将满足方程a*x1+b*y1=gcd(a,b)的一组x1和y1的值求了出来。下面代码中突出的部分就是标准的欧几里德算法的代码。
 
__int64 exGcd(__int64 a,__int64 b,__int64 &x,__int64 &y){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    __int64 g=exGcd(b,a%b,x,y);
    __int64 temp=x;
    x=y;
    y=temp-(a/b)*y;
    return g;
}
 
     2.那么x,y的一组解就是x1*m1,y1*m1,但是由于满足方程的解无穷多个,在实际的解题中一般都会去求解x或是y的最小正数的值。以求x为例,又该如何求解呢?还是从方程入手,现在的x,y已经满足a*x+b*y=m,那么a*(x+n*b)+b*(y-n*a)=m显然也是成立的。可以得出x+n*b(n=…,-2,-1,0,1,2,…)就是方程的所有x解的集合,由于每一个x都肯定有一个y和其对应,所以在求解x的时候可以不考虑y的取值。取k使得x+k*b>0,x的最小正数值就应该是(x+k*b)%b,但是这个值真的是最小的吗??如果我们将方程最有两边同时除以gcd(a,b),则方程变为a1*x+b1*y=m1,同上面的分析可知,此时的最小值应该为(x+k*b1)%b1,由于b1<=b,所以这个值一定会小于等于之前的值。在实际的求解过程中一般都是用while(x<0)x+=b1来使得为正的条件满足,为了更快的退出循环,可以将b1改为b(b是b1的倍数),并将b乘以一个倍数后再加到x上。

参考:

http://www.cnblogs.com/void/archive/2011/04/18/2020357.html

http://972169909-qq-com.iteye.com/blog/1538245

版权声明:本文为博主原创文章,未经博主允许不得转载。

扩展欧几里德 SGU 106的更多相关文章

  1. SGU 106 The equation 扩展欧几里德

    106. The equation time limit per test: 0.25 sec. memory limit per test: 4096 KB There is an equation ...

  2. 数论 + 扩展欧几里得 - SGU 106. The equation

    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< ...

  3. (扩展欧几里德算法)zzuoj 10402: C.机器人

    10402: C.机器人 Description Dr. Kong 设计的机器人卡尔非常活泼,既能原地蹦,又能跳远.由于受软硬件设计所限,机器人卡尔只能定点跳远.若机器人站在(X,Y)位置,它可以原地 ...

  4. [BZOJ1407][NOI2002]Savage(扩展欧几里德)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1407 分析: m,n范围都不大,所以可以考虑枚举 先枚举m,然后判定某个m行不行 某个 ...

  5. 欧几里德与扩展欧几里德算法 Extended Euclidean algorithm

    欧几里德算法 欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数. 基本算法:设a=qb+r,其中a,b,q,r都是整数,则gcd(a,b)=gcd(b,r),即gcd(a,b)=gcd( ...

  6. 51nod 1352 扩展欧几里德

    给出N个固定集合{1,N},{2,N-1},{3,N-2},...,{N-1,2},{N,1}.求出有多少个集合满足:第一个元素是A的倍数且第二个元素是B的倍数. 提示: 对于第二组测试数据,集合分别 ...

  7. CF 7C. Line(扩展欧几里德)

    题目链接 AC了.经典问题,a*x+b*y+c = 0整数点,有些忘记了扩展欧几里德,复习一下. #include <cstdio> #include <iostream> # ...

  8. poj2142-The Balance(扩展欧几里德算法)

    一,题意: 有两个类型的砝码,质量分别为a,b;现在要求称出质量为d的物品, 要用多少a砝码(x)和多少b砝码(y),使得(x+y)最小.(注意:砝码位置有左右之分). 二,思路: 1,砝码有左右位置 ...

  9. poj2115-C Looooops(扩展欧几里德算法)

    本题和poj1061青蛙问题同属一类,都运用到扩展欧几里德算法,可以参考poj1061,解题思路步骤基本都一样.一,题意: 对于for(i=A ; i!=B ;i+=C)循环语句,问在k位存储系统中循 ...

随机推荐

  1. 解决placeholder对ie9及以下的不兼容

    具体的看代码,都写在注释里了,欢迎讨论 <script> $(function () { /* *页面加载立刻执行 *@method:each遍历每个包含placeholder属性的对象( ...

  2. 微信小程序 - bindtap等事件传参

    什么是事件事件是视图层到逻辑层的通讯方式. 事件可以将用户的行为反馈到逻辑层进行处理. 事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数. 事件对象可以携带额外信息,如 id, ...

  3. windows下开启 PHP扩展Redis

    Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...

  4. [译]C语言实现一个简易的Hash table(4)

    上一章我们解释了Hash table中最重要的hash函数,并用伪代码和C语言实现了一个我们自己的hash函数,hash函数中碰撞是无法避免的,当发生碰撞时我们改如何有效的处理呢?这章我们就来讲解下. ...

  5. PTA基础编程题目集6-5求自定类型元素的最大值 (函数题)

    原题目: 本题要求实现一个函数,求N个集合元素S[]中的最大值,其中集合元素的类型为自定义的ElementType. 函数接口定义: ElementType Max( ElementType S[], ...

  6. 解决Linux上tomcat启动却无法访问

    linux中tomcat的安装 安装tomcat前首先要安装对应的jdk并配置Java环境 下载tomcat安装包 下载路径:https://tomcat.apache.org/download-80 ...

  7. 微信小程序真机预览跟本地不同的问题。原文地址:https://blog.csdn.net/qq_27187991/article/details/69664247/

    微信小程序中出现最多的一个问题,就是真机跟本地不同:我简单列举一些我发现的原因,给大家参考,大家也可以把自己发现的东西回复给我,给我参考: 本地看不到数据,就先让本地能看到数据,再看本帖....特别提 ...

  8. stardict词库

    http://download.huzheng.org/zh_CN/ sudo tar -jxvf *   -C /usr/share/stardict/dic

  9. [WC2010]重建计划 长链剖分

    [WC2010]重建计划 LG传送门 又一道长链剖分好题. 这题写点分治的人应该比较多吧,但是我太菜了,只会长链剖分. 如果你还不会长链剖分的基本操作,可以看看我的长链剖分总结. 首先一看求平均值最大 ...

  10. 【MySQL函数】replace()函数

    1.replace()函数 语法:replace(a,b,c) a的b部分被c替换 案例:select replace(abcde,bcd,123)-- 结果a123e