LightOJ - 1306 Solutions to an Equation
题目大意:给你一个方程ax+by+c=0(x1<=x<=x2&&y1<=y<=y2)
求其整数解个数
具体思路:扩展欧几里得求一个特解,然后求一下x和y的最少变化量,
然后求出当x1<=x<=x2,与其对应的y的范围,和y1<=y<=y2做一个并,在算一下有几个解就好了
花絮:一开始我发现x=x0+xx*t(x0为特解,xx为最少变化量)y=y0-yy*t,并试图求出t的范围,wa了几发之后发现有许多+1-1的奇怪地方需要处理,于是果断换了一个做法
结果却还是wa,通过一些特殊手段发现好像是我为了把方程变为ax+by=c,我把读入的c取反,但前面的特判写的比较早~~~
扩展欧几里得是什么勒?
欧几里得算法可以求最大公约数
int gcd(int a,int b){if(b==0)return a;else return gcd(b,a%b);}
然后扩展欧几里得可以求ax+by=gcd(a,b)的一组解
怎么求的呢?
因为gcd(a,b)=gcd(b,a%b)
所以ax+by=gcd(a,b)可以变为
b*x1+(a%b)*y1=gcd(b,a%b)=ax+by
然后一顿公式变形
b*x1+(a%b)*y1=b*x1+(a-[a/b]*b)*y1([]为下取整)
=b*x1+(a-[a/b]*b)*y1=a*y1+b*(x1-[a/b]*y1)=ax+by
所以x=y1 y=x1-[a/b]*y1
于是可以写成这样
void exgcd(int a,int b,int& d,int& x,int & y) { if(b==0)d=a,x=1,y=0; else exgcd(b,a%b,d,y,x),y-=(a/b)*x; }
最后是AC代码
#include<cstdio> #include<algorithm> #include<cmath> #define int long long using namespace std; int T; int gcd(int a,int b){if(b==0)return a;else return gcd(b,a%b);} void exgcd(int a,int b,int& d,int& x,int & y) { if(b==0)d=a,x=1,y=0; else exgcd(b,a%b,d,y,x),y-=(a/b)*x; } main() { scanf("%lld",&T); int t; for (t=1;t<=T;t++) { int a,b,c,X1,X2,Y1,Y2; scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&X1,&X2,&Y1,&Y2);c=-c; printf("Case %lld: ",t); if(a==0&&b==0) { if(c==0) printf("%lld\n",(Y2-Y1+1)*(X2-X1+1));else puts("0"); continue; } else if(a==0) { if(c%b!=0) puts("0"); else { int z=c/b; if(z>=Y1&&z<=Y2) printf("%lld\n",(X2-X1+1));else puts("0"); } continue; } else if(b==0) { int z=c/a; if(c%a!=0) puts("0"); else if(z>=X1&&z<=X2) printf("%lld\n",(Y2-Y1+1)); else puts("0"); continue; } int g=gcd(a,b); if(c%g) { puts("0"); continue; } int x,y; exgcd(a,b,g,x,y); x=x*(c/g),y=y*(c/g); int xx=b/g,yy=a/g,Ymn,Ymx; X1=((x-X1)%xx+abs(xx))%abs(xx)+X1; X2=((x-X2)%xx-abs(xx))%abs(xx)+X2; if(X1>X2){printf("0\n");continue;} Ymn=(-yy)*((X1-x)/xx)+y; Ymx=(-yy)*((X2-x)/xx)+y; if(Ymn>Ymx)swap(Ymn,Ymx); Y1=max(Y1,Ymn); Y2=min(Y2,Ymx); Y1=((y-Y1)%yy+abs(yy))%abs(yy)+Y1; Y2=((y-Y2)%yy-abs(yy))%abs(yy)+Y2; if(Y1>Y2)puts("0");else printf("%lld\n",(Y2-Y1)/abs(yy)+1); } return 0; }