sgu106

3 篇文章 0 订阅
1 篇文章 0 订阅

106. The equation

time limit per test: 0.5 sec.
memory limit per test: 4096 KB

There is an equation ax + by + c = 0. Given a,b,c,x1,x2,y1,y2 you must determine, how many integer roots of this equation are satisfy to the following conditions : x1<=x<=x2,   y1<=y<=y2. Integer root of this equation is a pair of integer numbers (x,y).

Input

Input contains integer numbers a,b,c,x1,x2,y1,y2 delimited by spaces and line breaks. All numbers are not greater than 108 by absolute value.

Output

Write answer to the output.

Sample Input

1 1 -3
0 4
0 4

Sample Output

4

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

这道题的做法是欧几里德扩展。

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;以此做返回值。


我们写出一个扩展欧几里德算法的函数

int extendedGcd(int a,int b){
    if (b==0){
	 x=1;y=0;
	 return a;
    }
    else{
	 int tmp = extendedGcd(b,a%b);
	 int t = x;
	 x=y;
	 y=t-a/b*y;
	 return tmp;
    }
}

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即可。


呼,写完了,希望大家看得懂。


代码参考:http://www.cppblog.com/schindlerlee/archive/2009/11/26/101934.html

/*
 *NAME:The Equation
 *LANG:C++
 *Source:sgu106
 */
#include <iostream>
using namespace std;
typedef long long LL;
LL a,b,c,x1,x2,y1,y2,x,y,tmp,ans=0;
LL mini = -361168601842738790LL;
LL maxi = 322337203685477580LL;
int extendedGcd(int a,int b){
    if (b==0){
	x=1;y=0;
	return a;
    }
    else{
	int tmp = extendedGcd(b,a%b);
	int t = x;
	x=y;
	y=t-a/b*y;
	return tmp;
    }
}
LL extendedGcd(LL a,LL b){
    if (b == 0){
	x=1;y=0;
	return a;
    }
    else{
	LL TEMP = extendedGcd(b,a%b);
	LL tt=x;
	x=y;
	y=tt-a/b*y;
	return TEMP;
    }
}
LL upper(LL a,LL b){
    if (a<=0)
	return a/b;
    return (a-1)/b + 1;
}
LL lower(LL a,LL b){
    if (a>=0) 
	return a/b;
    return (a+1)/b - 1;
}
void update(LL L,LL R,LL wa){
    if (wa<0){
	L=-L;R=-R;wa=-wa;
	swap(L,R);
    }
    mini=max(mini,upper(L,wa));
    maxi=min(maxi,lower(R,wa));
}
int main(){
    cin >> a >> b >> c >> x1 >> x2 >> y1 >> y2;c=-c;
    if (a==0 && b==0){
	if (c==0) ans = (x2-x1+1) * (y2-y1+1);
    }
    else if (a==0 && b!=0){
	if (c % b==0) {
	    tmp = c/b;
	    if (tmp>=y1 && tmp<=y2) ans = 1;
	}
    }
    else if (a!=0 && b==0){
	if (c % a==0){
	    tmp = c/a;
	    if (tmp>=x1 && tmp<=x2) ans = 1;
	}
    }
    else{
	LL d = extendedGcd(a,b);
	if (c%d == 0){
	    LL p = c/d;
	    update(x1-p*x,x2-p*x,b/d);
	    update(y1-p*y,y2-p*y,-a/d);
	    ans = maxi-mini+1;
	    if (ans<0) ans=0;
	}
    }
    cout << ans << endl;
}




评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值