比较经典的扩展欧几里德求整数解的问题
对于两个点(x1,y1)(x2,y2),如果点为整数点,那么必定能通过扩展欧几里德求出区间中的整点个数,即对方程(y2-y1)x+(x1-x2)y=x1y2-x2y1 求解。
由于此题的点精确到小数后1位,我们可以将x1,y1,x2,y2分别放大10倍,问题转变成对方程[10*(y2-y1)]x+[10(x1-x2)]y=100*(x1y2-x2y1)求解。
记a=10*(y2-y1),b=10*(x1-x2) ,c=100*(x1y2-x2y1), g=gcd(a,b)
那么用扩展欧几里德可以得到aX+bY=gcd(a,b)的一组解。注意,这里得到的一组解仅保证abs(X+Y)取到最小值
判断需要求解的方程是否有解,只需要判断c%gcd(a,b)==0是否成立即可。
那么就可以得到需要求解的方程的一组解:X0=X*c/g, Y0=Y*c/g
此时就可以对区间有几个解进行计算了。
另外,因为之前扩展欧几里德写的少,所以xjb写了一发暴力,此处贴上代码。
正解
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int T;
LL x[2], y[2];
void read(LL &x)
{
LL a, b;
scanf("%lld.%lld", &a, &b);
x = a * 10 + b;
}
void exgcd(LL a, LL b, LL& g, LL& x, LL& y)
{
if (!b) g = a, x = 1, y = 0;
else exgcd(b, a%b, g, y, x), y -= x * (a / b);
}
LL solve()
{
LL ans = 0;
for (int i = 0; i < 2; i++)read(x[i]), read(y[i]);
if (x[0] > x[1] || x[0] == x[1] && y[0] > y[1]) {
swap(x[0], x[1]);
swap(y[0], y[1]);
}
if (x[0] == x[1]) {
if (x[0] % 10)return 0;
return max(0LL, y[1] / 10 - (y[0] + 9) / 10 + 1);
}
if (y[0] == y[1]) {
if (y[0] % 10)return 0;
return max(0LL, x[1] / 10 - (x[0] + 9) / 10 + 1);
}
LL a, b, g, X, Y;
a = (y[1] - y[0]) * 10;
b = (x[0] - x[1]) * 10;
exgcd(a, b, g, X, Y);
LL c = x[0] * y[1] - x[1] * y[0];
if (c%g)return 0;
X = X * c / g;
x[0] = (x[0] + 9) / 10;
x[1] = x[1] / 10;
if (x[0] > x[1])return 0;
LL st = abs(b / g);
LL cx = X - (X - x[0]) / st * st;
if (cx < x[0])cx += st;
if (cx > x[1])return 0;
return (x[1] - cx) / st + 1;
}
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
scanf("%d", &T);
while (T--) {
printf("%lld\n", solve());
}
}
暴力
#include<bits/stdc++.h>
using namespace std;
typedef long long LL ;
int T;
LL x[2], y[2];
LL gcd(LL a, LL b)
{
return b == 0 ? a : gcd(b, a%b);
}
void read(LL &x)
{
LL a, b;
scanf("%lld.%lld", &a, &b);
x = a * 10 + b;
}
int main()
{
scanf("%d", &T);
while (T--) {
for (LL i = 0; i < 2; i++)read(x[i]), read(y[i]);
/*for (int i = 0; i < 2; i++) {
x[i] = (rand() % 200 + 1) * 10;
y[i] = (rand() % 200 + 1) * 10;
}*/
//printf("%d %d %d %d\n", x[0], y[0], x[1], y[1]);
if (x[0] > x[1] || x[0] == x[1] && y[1] > y[0]) {
swap(x[0], x[1]);
swap(y[0], y[1]);
}
if (x[0] == x[1] && (x[0] % 10) || y[0] == y[1] && (y[0] % 10)) {
printf("0\n");
continue;
}
if (x[0] == x[1] && y[0] == y[1]) {
if (x[0] % 10 == 0 && y[0] % 10 == 0) {
printf("1\n");
}
else printf("0\n");
continue;
}
LL dx = abs(x[0] - x[1]);
LL dy = abs(y[0] - y[1]);
LL g = gcd(dx, dy);
LL st = dx / g;
LL cx = x[0], cy = y[0];
LL ans = 0;
for (LL i = 0; i <= 100 && cx <= x[1] && (cy - y[0])*(cy - y[1]) <= 0; i++) {
if (cx % 10 == 0 && cy % 10 == 0) {
long long gc = gcd(gcd(st, 10), gcd(dy / g, 10));
LL t = 10 / gcd(gcd(st, 10), gcd(dy / g, 10));
if (st)ans = 1 + (x[1] - cx) / (t*st);
else ans = abs(y[1] - cy) / (dy / g * t) + 1;
break;
}
cx += st;
cy += (y[1] - y[0]) / g;
}
printf("%lld\n", ans);
}
}