http://lightoj.com/volume_showproblem.php?problem=1306
https://cn.vjudge.net/problem/LightOJ-1306
有形如 Ax+By+C=0 A x + B y + C = 0 的方程,给定 A A 、 B B 、 C C ,求 x x 、 y y 在 x1 x 1 到 x2 x 2 , y1 y 1 到 y2 y 2 间的解数。
(所有数都为整数,数据范围 −108 − 10 8 到 108 10 8 ,最多 10000 10000 组数据)。
首先,我们抛开 x x 、 y y 的范围限制,仅仅是求解,那么我们可以用扩展欧几里得算法来解决。下面详细介绍如何求出一组解。
若 gcd(A,B)∤C g c d ( A , B ) ∤ C ,那么无解。
设在此情况下问题有解,解为 x x , y y 。
设 gcd(A,B)=t g c d ( A , B ) = t ,那么 t|A t | A , t|B t | B ,所以 t|Ax t | A x , t|By t | B y ,所以 t|(Ax+By) t | ( A x + B y ) ,推出 t|C t | C ,而 gcd(A,B)∤C g c d ( A , B ) ∤ C ,矛盾
命题得证
我们将 A A 、 B B 、 C C 都除以 gcd(A,B) g c d ( A , B ) 。由等式的性质可得,这样做不影响答案。现在 A A 、 B B 互质了。
我们不妨考虑 Ax+By=1 A x + B y = 1 的情况。
因为 gcd(A,B)=1 g c d ( A , B ) = 1 ,所以 gcd(B,AmodB)=1 g c d ( B , A mod B ) = 1 。(将 A A 分为 [AB]×B [ A B ] × B 和余数部分,用反证法易得。)
所以 Bx′+(AmodB)y′=1 B x ′ + ( A mod B ) y ′ = 1 同样有解。我们观察这两个式子的联系。
我们记特解为 x0 x 0 , y0 y 0 。那么通解就是
那么我们要求的就是符合条件的 t t 的个数。不如将 x x , y y 分开考虑,那么就是求两个可行区间的交。这一部分的细节极多,严格操作很难直接说清楚,大家不如直接看程序,里面有详细的注释和说明。
#include
#define LL long long
using namespace std;
LL A, B, C, x1, x2, y1, y2;
LL xl, xr, yl, yr;
void swap(LL & x, LL & y) {//交换两个数
LL t = x; x = y; y = t;
return;
}
LL gcd(LL x, LL y) {//求最小公约数
LL m = x % y;
while(m) { x = y; y = m; m = x % y; }
return y;
}
void exgcd(LL A, LL B, LL & x, LL & y) {//求不定方程一组特解(Ax+By=1)
if(B == 0) {
if(A == 1) x = 1; else x = -1;
y = 0; return;
}
exgcd(B, A % B, y, x);
y -= A / B * x;
return;
}
LL xxl, xxr, yyl, yyr;
void work2() {//特判0的情况
if(A == 0 && B == 0) {
if(C != 0) printf("0\n"); else printf("%lld\n", (x2 - x1 + 1) * (y2 - y1 + 1));
return;
}
if(B == 0) {
if(C % A != 0) {
printf("0\n");
return;
}
LL t = C / A;
if(x1 <= t && t <= x2) printf("%lld\n", y2 - y1 + 1); else printf("0\n");
return;
}
if(A == 0) {
if(C % B != 0) {
printf("0\n");
return;
}
LL t = C / B;
if(y1 <= t && t <= y2) printf("%lld\n", x2 - x1 + 1); else printf("0\n");
return;
}
}
void work() {
scanf("%lld%lld%lld%lld%lld%lld%lld", &A, &B, &C, &x1, &x2, &y1, &y2);
C = -C;
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
if(B == 0 || A == 0) {//特判0的情况
work2();
return;
}
LL t = gcd(A, B);
if(t < 0) t = -t;
if(C % t != 0) {
printf("0\n");
return;
}
A /= t; B /= t; C /= t;//使A,B,C互质
LL x0, y0;
exgcd(A, B, x0, y0);
x0 *= C; y0 *= C;//求出一组特解x0,y0
xxl = x1 - x0; xxr = x2 - x0;//求出x1,x2相对与x0的距离
yyl = y1 - y0; yyr = y2 - y0;//同上
xl = xxl / B; xr = xxr / B;//求出满足x关系式的大致的t的范围(左右边界可能有1的偏差)
yl = yyl / (-A); yr = yyr / (-A);//同上
if(xl * B < xxl) xl += (B > 0) ? 1 : -1;//调整左边界
if(xr * B > xxr) xr -= (B > 0) ? 1 : -1;//调整右边界
if(yl * (-A) < yyl) yl += ((-A) > 0) ? 1 : -1;//同上
if(yr * (-A) > yyr) yr -= ((-A) > 0) ? 1 : -1;
if(x0 + B * xl > x2 || x0 + B * xr < x1) { printf("0\n"); return; } //若不存在这样的t使得x处于x1,x2间
if(y0 - A * yl > y2 || y0 - A * yr < y1) { printf("0\n"); return; } //同上
if(xl > xr) swap(xl, xr);//再次判断原因说明:
// 由于我们除的系数有负,所以我们在这儿的"左",并不代表坐标较小的那个。而是有关于B,-A的正负性。
// 这个在前面处理的时候千万别搞错,后面为了方便判断交集,所以规定正常意义下的大小
if(yl > yr) swap(yl, yr);
// 分类讨论,确定交集
if(xl < yl) {
if(xr < yl) { printf("0\n"); return; }
if(yl <= xr && xr < yr) { printf("%lld\n", xr - yl + 1); return; }
if(yr <= xr) { printf("%lld\n", yr - yl + 1); return; }
}
if(yl <= xl && xl <= yr) {
if(xr < yr) { printf("%lld\n", xr - xl + 1); return; }
if(yr <= xr) { printf("%lld\n", yr - xl + 1); return; }
}
if(xl > yr) { printf("0\n"); return; }
}
int main() {
LL t;
scanf("%lld", &t);
for(LL i = 1; i <= t; i++) printf("Case %lld: ", i), work();
return 0;
}