LightOJ 1306 Solutions to an Equation 数论

题目链接

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 C C ,求x y y x1 x2 x 2 y1 y 1 y2 y 2 间的解数。
(所有数都为整数,数据范围 108 − 10 8 108 10 8 ,最多 10000 10000 组数据)。


解题思路

首先,我们抛开 x x y的范围限制,仅仅是求解,那么我们可以用扩展欧几里得算法来解决。下面详细介绍如何求出一组解。

拓展欧几里得求出一组特解

无解情况

gcd(A,B)C g c d ( A , B ) ∤ C ,那么无解。

证明

设在此情况下问题有解,解为 x x 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 C C 都除以gcd(A,B)。由等式的性质可得,这样做不影响答案。现在 A A 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和余数部分,用反证法易得。)
所以 Bx+(AmodB)y=1 B x ′ + ( A mod B ) y ′ = 1 同样有解。我们观察这两个式子的联系。

Bx+(AmodB)y=1 B x ′ + ( A mod B ) y ′ = 1

Bx+(A[AB]×B)y=1 ⇒ B x ′ + ( A − [ A B ] × B ) y ′ = 1

Bx+Ay[AB]×By=1 ⇒ B x ′ + A y ′ − [ A B ] × B y ′ = 1

Ay+B(1[AB])x=1 ⇒ A y ′ + B ( 1 − [ A B ] ) x ′ = 1

就有
x=yy=x[AB]x x = y ′ y = x ′ − [ A B ] x ′

那么我们不断迭代这一操作,直到 B=0 B = 0 ,那么此时 A=1 A = 1 (可由互质推出)。所以我们可取 x=1 x = 1 y=0 y = 0 ,再退回去,就得到了原式的一组解。

由一组特解求解数

我们记特解为 x0 x 0 y0 y 0 。那么通解就是

x=x0+Bgcd(A,B)ty=y0Agcd(A,B)t(tZ) x = x 0 + B g c d ( A , B ) t y = y 0 − A g c d ( A , B ) t ( t ∈ Z )

由于 gcd(A,B)=1 g c d ( A , B ) = 1 ,所以
x=x0+Bty=y0At(tZ) x = x 0 + B t y = y 0 − A t ( t ∈ Z )

那么我们要求的就是符合条件的 t t 的个数。不如将x y y <script type="math/tex" id="MathJax-Element-55">y</script>分开考虑,那么就是求两个可行区间的交。这一部分的细节极多,严格操作很难直接说清楚,大家不如直接看程序,里面有详细的注释和说明。


参考程序

#include <cstdio>
#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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值