扩展欧几里德 SGU 106
2014-07-24 23:10
218 查看
[b]题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=106[/b]
题意:求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;
这里讲下:
欧几里德算法的扩展-求解不定方程
扩展欧几里德算法是用来在已知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) 即可。
参考:
http://www.cnblogs.com/void/archive/2011/04/18/2020357.html
http://972169909-qq-com.iteye.com/blog/1538245
题意:求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) 即可。
http://www.cnblogs.com/void/archive/2011/04/18/2020357.html
http://972169909-qq-com.iteye.com/blog/1538245
相关文章推荐
- SGU 106. The equation 扩展欧几里德
- 扩展欧几里德 SGU 106
- SGU 106 The equation(扩展欧几里德)
- sgu106 The Equation 扩展欧几里德
- SGU 106 The equation(扩展欧几里德)
- SGU 106 扩展欧几里得
- SGU 106 The equation 【扩展欧几里得算法】
- SGU 106 The equation 扩展欧几里得好题
- SGU 106 The Equation 扩展欧几里得应用
- SGU 106 The equation(扩展欧几里得)
- SGU 106 The equation ----扩展欧几里得
- SGU 106 The equation 扩展欧几里德
- 数论 + 扩展欧几里得 - SGU 106. The equation
- sgu106 扩展欧几里得
- SGU 106 The equation(扩展欧几里得)
- sgu106(扩展欧几里得)
- SGU - 106 - The equation (扩展欧几里得)
- SGU 106 The equation (GCD·我所理解的扩展欧几里得)
- The equation - SGU 106(扩展欧几里得)
- 扩展欧几里得算法的应用 POJ 2115 POJ 2142 POJ 1061 HDU 2669 HDU 1576 SGU 106