直线上的点(数论初步) By ACReaper
首先我们由欧几里德拓展算法可以求得一组解(d,x,y),而这组解通过与ax + by + c = 0中的c / d,如果c是d的倍数则这个点是直线上的一个解。
分析:
设a,b,c为任意整数,g = gcd(a,b),方程ax + by = g的一组解是(x0,y0),则当c是g的倍数时ax + by = c的一组解是(x0*c/g,y0*c/g),当c不是g的倍数时无整数解。
接着我们由这组解就可以求出其它解,如何求呢?分析如下,设基底解为(x0,y0),另外一个解为(x1,y1),则ax0 + by0 = ax1 + by1 = c,整理后有a(x0 - x1) = b(y1 - y0),这里我们在同时除以gcd(a,b),记a' = a / gcd(a,b),b' = b / gcd(a,b),则a(x0 - x1) = b'(y1 - y0),这时a‘,b’一定是互素的,也就是此时他们的最大公约数为1,因为刚刚约掉了他们的最大公约数,所以x0 - x1一定是b‘的倍数,设x0 - x1 = kb’,有a‘ * kb’ = b‘(y1 - y0),有y1 = y0 + ka’,同理易得x1 = x0 - kb‘。
实现如下:
#include <stdio.h> void gcd(int a,int b,int &d,int &x,int &y){ if(b == 0){ d = a,x = 1, y = 0; }else{ int x1,y1; gcd(b,a % b,d,x1,y1); x = y1; y = x1 - (a / b) * y1; } } int main(){ int a,b,c; int xx[2];//x的范围 int yy[2];//y的范围 while(scanf("%d%d%d%d%d%d%d",&a,&b,&c,xx,xx + 1,yy,yy + 1) != EOF){ int d,x,y; gcd(a,b,d,x,y); if(c % d == 0){//求得方程的一组解 x *= c /d; y *= c /d; }else{ printf("无解"); continue; } int st = (x - xx[0]) / (b / d);//求出k的取值范围 int ed = (x - xx[1]) / (b / d); int A[100]; int count = 1; A[0] = 0; for(int k = st; k <= ed; k++){//循环推出所有复合的解 int temp = y + k * (a / d); if(temp >= yy[0] && temp <= yy[1]){ A[count++] = k; } } printf("该范围内一共有%d个解\n",count); for(int i = 0 ; i < count; i++){ printf("%d %d\n",x - A[i] * (b / d),y + A[i] * (a / d)); } } return 0; }