hihocoder1584 Bounce 2017icpc-北京赛区 扩展欧几里德

https://hihocoder.com/problemset/problem/1584

题意:在n*m的网格上,一颗小球左上方出发往右下的格子走,碰到边界就反弹,当小球到达角落就停止运动。求小球只经过一次的格子的数量。

题解:

第一步:如图(3*4为例),可以把每次反弹后的线路展开,每次反弹就增加m-1(碰到上下边界)或者n-1(碰到左右边界)。因为小球是45度方向,所以最后展开一定是正方形。于是可以列出不定方程(m-1)*x+m=(n-1)*y+n。其中x和y可以通过扩展欧几里德求出最小正数解(m-1)*x+m就是小球经过的格子总数(经过同一个两次的算两次)。

第二步:然后要减去经过两次的格子。x就是左右反弹次数之和,y就是上下反弹次数之和。相交的就是经过两次的要减去。可以从样例图的交点看出,数量就是x*y(结合样例图与反弹次数有关,因为每次反弹会与另一个方向的路线有相交)。

那么答案就是(m-1)*x+m-x*y。


画的有点丑。最后是要变成一个正方形。


样例图

代码:

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<string>
#include<bitset>

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>

#include<iomanip>
#include<iostream>

#define debug cout<<"aaa"<<endl
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define MIN_INT (-2147483647-1)
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807i64
#define MIN_LL (-9223372036854775807i64-1)
using namespace std;

const int N = 100000 + 5;
const int mod = 1000000000 + 7;
//扩展欧几里德
LL exgcd(LL a,LL b,LL &x,LL &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	LL r=exgcd(b,a%b,x,y);
	LL t=x;
	x=y;
	y=t-a/b*y;
	return r;
} 

int main(){
    int n,m;
	LL x,y,gcd,temp,num;
	while(~scanf("%d%d",&n,&m)){
		gcd=exgcd(m-1,1-n,x,y);
		x=x*(n-m)/gcd;
		temp=(1-n)/gcd;
		if(temp<0){
			temp=-temp;
		}
		//扩欧出来的解可能是负的,要求出最小正数解 
		if(x<0){
			x=x+(-x/temp+1)*temp;
		}
		else{
			x%=temp;
		}
		y=((m-1)*x+m-n)/(n-1);
		printf("%lld\n",(m-1)*x+m-x*y);
	}
	return 0;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值