题目大意:给定a,b,c,x1,x2,y1,y2,求满足a*x+b*y+c = 0的解x满足x1<=x<=x2,y满足y1<=y<=y2.求满足条件的解的个数。
题目链接:
http://acm.sgu.ru/problem.php?contest=0&problem=106 让人又爱又恨的一道题……调了一下午,各种对拍,还发现网上的一份AC代码错了>.<……很看细节的一道题。我们来一步步分析~ 我们先令c = -c,转化为我们熟悉的线性方程ax + by = c.然后: ①用扩展欧几里德找到一组解(x0,y0),这个很简单。但也有需要注意的地方,比如
a,b,c=0的情况需要特殊注意一下,虽然扩展欧几里德可以解出a,b,c为任何值的一组解,但是这些情况在寻求解的个数的时候还是和一般方程不太一样的。 ②我们也知道方程的通解为: x = x0 + kb' (1); y = y0 - ka' (2) ; (b' = b/gcd(a,b), a' = a / gcd(a,b) ). 那么sgu 106就是求在范围内k能取几个: ③将x1,x2带入上面(1)方程求出k1,k2 (k1 < k2); 同理将y1, y2带入上面(2)方程求出k3, k4 (k3 < k4).则
ans = min(k2, k4) - max(k1, k3).(这个min,max,等式一时半会儿也不好解释清楚,最好还是在纸上比划比划验证一下……). 而且,注意到我们是没把x0,y0解算进去的,于是当x0,y0也在范围内时,ans+1. ④细节:
边界问题:例如,如果x0 = 3, x1 = 5, x2 =7, b = 2, 那么k2 - k1 = 2 - 1 = 1,实际上[x1..x2]内是可以取到两个k的~~应该看出什么了,当xo < x1 < x2时,计算k1时要把x1-1,同理当x1 < x2 < x0时,计算k2时要把x2+1;对y0, y1, y2同理。 代码各种补丁,有点儿乱……
#include
#include
#include
using namespace std; long long gcd(long long a, long long b){ if (b == 0) return a; return gcd(b, a%b); } void ext_gcd(long long a, long long b, long long &x, long long &y){ if (b == 0){ x = 1; y = 0; return ; } ext_gcd(b, a%b, x, y); long long tmp = x; x = y; y = tmp - a / b * y; return ; } long long num_of_equation(long long x0, long long y0, long long x1, long long y1, long long x2, long long y2, long long a, long long b){ long long xx1 = x1, xx2 = x2, yy1 = y1, yy2 = y2; //保留区间,最后判断x0,y0是否在区间内需要 //边界处理: if (x0 > x2) x2 ++; if (x0 < x1) x1 --; if (y0 > y2) y2 ++; if (y0 < y1) y1 --; long long k1 = (x1 - x0)/b; long long k2 = (x2 - x0)/b; if (k1 > k2){ long long tmp = k1; k1 = k2; k2 = tmp; } long long k3 = (y1 - y0)/-a; long long k4 = (y2 - y0)/-a; if (k3 > k4){ long long tmp = k3; k3 = k4; k4 = tmp; } long long res = min(k2, k4) - max(k1, k3); if (x0 >= xx1 && x0 <= xx2 && y0 >= yy1 && y0 <= yy2) res ++; return res < 0 ? 0 : res; } long long f(long long a, long long b, long long c, long long x1, long long x2, long long y1, long long y2){ c *= -1; //把c移到等式右边构成ax+by=c //特殊处理a=b=0,a=0 b!=0,a!=0 b=0的情况 if (a == 0 && b == 0){ if (c == 0) return (x2 - x1 + 1) * (y2 - y1 + 1); else return 0; } if (a == 0){ if (c % b == 0) if (c/b >= y1 && c/b <= y2) return (x2 - x1 + 1); else return 0; else return 0; } if (b == 0){ if (c % a == 0) if (c/a >= x1 && c/a <= x2) return (y2 - y1 + 1); else return 0; else return 0; } //一般情况: long long x0, y0, flagx = 1, flagy = 1, g = gcd(a, b); if (c % g != 0) return 0; a /= g; if (a < 0) a = -1*a, flagx = -1; //把a,b都变正数(不过感觉不变也行……囧) b /= g; if (b < 0) b = -1*b, flagy = -1; c /= g; ext_gcd(a, b, x0, y0); x0 *= (c * flagx); //解出了一组特殊解x0, y0 y0 *= (c * flagy); a *= flagx; b *= flagy; return num_of_equation(x0, y0, x1, y1, x2, y2, a, b); } int main(){ long long a, b, c, x1, x2, y1, y2; cin >> a >> b >> c >> x1 >> x2 >> y1 >> y2; cout << f(a, b, c, x1, x2, y1, y2) << endl; return 0; }