1213解的个数——codevs【exgcd】

本文探讨了数论中的基本概念,特别是通过扩展欧几里得定理来解决形如ax+by+c=0的线性方程组。文中详细解释了如何通过该定理找到一组最小符合要求的解,并在此基础上拓展出一系列解,同时考虑了解的范围限制。此外,文章还提出了在特定区间内求解的方法,并通过实例代码演示了实现过程。

” 数论只会gcd“      系列


ax+by+c=0
变形ax+by=-c

用拓展欧几里得定理做

找出一组最小符合要求解之后

再来拓展出其他在pq  rs 范围内的数

变形 a(x+b)+b(y-a)=-c
这样可以拓展出一些其他的解  但是这样并不是最多

会漏掉一些解

因为有一个比a,b更小的常数可以用来加减

设a(x+u)+b(y-v)=-c

ax+by+au-bv=-c

因为ax+by=-c

得到
au-bv=0

au=bv

u=v*b/a

要使u是最小正整数

即是v*b/a最小整数

去掉分母就是目的了

当然去掉的a是越小越好了   这样b也越小   b*v的整数就越小

就设g=gcd(a,b)

u=v*(b/g)/(a/g)

得到互质的a/g  b/g

再变换回来(刚才过程是为了推导。)

u*(a/g)=v*(b/g)

要使两边的数  乘上一个互质的数  相等

就是要让  左边的因子  和右边的因子  一样多 一样大

而(a/g)与(b/g)互质

就只有从uv上找因子互补了

对于(a/g)  缺少的是  (b/g)    u=b/g
对于(b/g)  缺少的是  (a/g)    v=a/g

于是  a[ x+(b/g) ] + b[ y-(a/g) ]  = -c
或者 a[ x-(b/gb[ y+(a/g]  = -c   (两个方向 变大变小拓展)

结束条件   就保存上一次的x或者y

如果当前的值减去区间中位数的abs相比上一次变大了  却又不在区间之内
就break

于是就ok了  


吐槽codevs    数据居然有系数0。。
                                还有p>q   说好的[p,q]区间呢 。。

好吧   可能是我自己思维不够  orzorz 

#include<cstdio>

#define abs(a) ((a<0)?(-a):(a))
#define LL long long
using namespace std;

LL n,now,ans;

LL bx,by;

LL a,b,c,p,q,r,s;

LL x,y;

LL temp;

void readdata(){
	
	scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&p,&q,&r,&s);
	
}

LL gcd(LL a,LL b){
	
	if(b)
	
	return gcd(b,a%b);
	
	else return a;
	
}

void ex_gcd(LL aa,LL bb,LL &x,LL &y){	

	if(bb){		
	
		ex_gcd(bb,aa%bb,x,y);	
			
		temp=x;		
		
		x=y;		
		
		y=temp-aa/bb*y;	
			
	}	
	
	else x=-c/now,y=0;	
	
}

void solve(){
	
	ans=0;
	
	if(p>q||r>s){
		return;
	}
	
	if(a==0&&b==0){
		
		if(c==0){
			
			LL hehe=q-p;
			LL hehehe=s-r;
			ans=(abs(hehe)+1)*(abs(hehehe)+1);
			
		}
		return;
		
	}
	else{
		
		if(a==0){
			
			if((-c/b)>=r&&(-c/b)<=s)
			if(-c/b*b==-c)
			ans=q-p+1;
			return;
			
		}
		else{
			
			if(b==0){
				
				if((-c/a)>=p&&(-c/a)<=q)
				if(-c/a*a==-c)
				ans=s-r+1;
				return;
				
			}
			
		}
		
	}
	
	now=gcd(a,b);
	
	if(-c%now){
		return;
	}
	
	ex_gcd(a,b,x,y);
	
	LL midpq=(q+p)/2;
	LL midrs=(r+s)/2;	
	
	bx=x;
	by=y;
	
	if(bx>=p&&bx<=q&&by>=r&&by<=s){
		
		ans++;
		
	}	
	
	LL jx=x;
	LL jy=y;
	
	LL tx2,tx1;
	LL ty2,ty1;
	
	while(1){
		
		bx+=b/now;
		by-=a/now;
		
		if(bx>=p&&bx<=q&&by>=r&&by<=s){
			
			ans++;
			
		}
		else{
			
			tx1=jx-midpq;tx2=bx-midpq;
			ty1=jy-midrs;ty2=by-midrs;
			
			if(abs(tx1)<abs(tx2)&&abs(ty1)<abs(ty2)){
				
				break;
				
			}
			
			jx=bx;
			jy=by;
		}
		
	}
	
	jx=x;
	jy=y;
	while(1){
		
		x-=b/now;
		y+=a/now;
		
		if(x>=p&&x<=q&&y>=r&&y<=s){
			
			ans++;
			
		}
		else{
			
			tx1=jx-midpq;tx2=x-midpq;
			ty1=jy-midrs;ty2=y-midrs;
			
			if(abs(tx1)<abs(tx2)&&abs(ty1)<abs(ty2)){
				
				break;
				
			}
			
			jx=x;
			jy=y;
			
		}
		
	}
	


}

void print(){
	
	printf("%lld\n",ans);
	
}

int main(){
	
	scanf("%lld",&n);
	
	while(n--){
		
		readdata();		
		
		solve();
		
		print();
		
	}
	
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值