题目链接: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. 《Redis设计与实现》- 数据库

    1. 服务器中数据库结构 Redis 服务器将所有数据库都保存在服务器状态 redisServer 结构的 db 数组中,由 redisDb 结构代表一个数据库 struct redisServer ...

  2. PHP接收post请求,不是空数组就是没值,怎么办!

    使用: $_POST $_REQUEST I('post.') 都不行, 换成: file_get_contents("php://input");

  3. Delphi XE5的Android开发平台搭建

    Delphi XE5支持Android ARM的开发,可以在Android虚拟机里运行,因此建议将XE5安装在64bit的Windows,内存可以大于3GB Delphi XE5安装光盘中包含了最基本 ...

  4. Zeta--S3 Linux优化/缩短开机时间

    U-Boot1)axp20_set_ldo3实现里面把两个__msdelay(200);去掉,节省400ms2)sys_config.fex把下面的used设置为0,不使用开机指示灯闪烁,可以省掉35 ...

  5. 批处理之 for /f 中的delims和tokens

    0x00 前言 今天在对windows进行提权之前的系统信息收集的时候,需要使用到一条批处理语句把特定部分的内容从一个txt的文本当中提取出来:该条语句是如下: for /f "tokens ...

  6. LIFO栈 ADT接口 实现十进制转其他进制

    LIFO 接口 Stack.h //LIFO 链栈初始化 void InitStack(Stack top){ //LIFO 链栈判断栈空 boolean StackKEmpty(Stack top) ...

  7. R语言学习笔记—朴素贝叶斯分类

    朴素贝叶斯分类(naive bayesian,nb)源于贝叶斯理论,其基本思想:假设样本属性之间相互独立,对于给定的待分类项,求解在此项出现的情况下其他各个类别出现的概率,哪个最大,就认为待分类项属于 ...

  8. ubuntu下pycharm快捷方式创建

    终端输入:sudo gedit /usr/share/applications/Pycharm.desktop 粘贴模板: [Desktop Entry] Type=Application Name= ...

  9. 20155302 实验三 敏捷开发与XP实践

    20155302 实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验内容及步骤 (一)编码标准 在IDEA中使用工具(Code->Reformate Code)把代码重 ...

  10. 20155318 2016-2017-2 《Java程序设计》第一周学习总结

    20155318 2016-2017-2 <Java程序设计>第一周学习总结 教材学习内容总结 上周总结 上周学习了一些大学的学习方法,比如知识分为为三种:元知识.软知识和硬知识,讲述技能 ...