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;
}