一道不知道哪里来的数论题

Pro:
在一张\(n*m\)的台球桌上上打台球,初始位置\(x,y\),初速度\(v_x,v_y\),打到边缘会无能量损失的反弹。
问最少反弹多少次可以进洞。
Sol:
考虑把台球桌无限延伸。
然后发现可以用剩余系来搞。
大概就是

\[\begin{align*} x+v_x*t&=0 \ (mod\ n) \\ y+v_y*t&=0 \ (mod\ m) \end{align*} \]

即:

\[\begin{align*} v_x*t&=k_1*n-x \\ v_y*t&=k_2*m-y \end{align*} \]

联立消一下\(t\)

\[\begin{align*} (n*v_y)*k_1-(m*v_x)*k_2=x*v_y-y*v_x \end{align*} \]

然后需要最小化\(k_1+k_2-2\)
\(exgcd\)冲一下就行了

#include<bits/stdc++.h>
#define N 110000
#define db double
#define ll long long
#define ldb long double
#define ull unsigned long long
using namespace std;
const ll h=3,ki=149,mo=998244353;
const double pi=acos(-1),eps=1e-7,inf=1e18+7;
ll mod(ll x){return (x%mo+mo)%mo;}
ll inc(ll x,ll k){x+=k;return x<mo?x:x-mo;}
ll dec(ll x,ll k){x-=k;return x>=0?x:x+mo;}
ll ksm(ll x,ll k)
{
	ll ans=1;
	while(k){if(k&1)ans=1ll*ans*x%mo;k>>=1;x=1ll*x*x%mo;}
	return mod(ans);
}
ll inv(ll x){return ksm(x,mo-2);}
ll read()
{
	char ch=0;ll x=0,flag=1;
	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();}
	return x*flag;
}
void write(ll x)
{
	if(!x)return (void)putchar(48);
	if(x<0)putchar(45),x=-x;
	ll len=0,p[20];
	while(x)p[++len]=x%10,x/=10;
	for(ll i=len;i>=1;i--)putchar(p[i]+48);
}
void writeln(ll x){write(x),putchar('\n');}
ll X,Y;
ll exgcd(ll a,ll b)
{
	if(!b){X=1;Y=0;return a;}
	ll d=exgcd(b,a%b);
	ll t=X;X=Y;Y=t-(a/b)*Y;
	return d;
}
void work()
{
	ll n=read(),m=read(),x=read(),y=read(),vx=read(),vy=read(); 
	if(!vx||!vy){printf("NO\n");return;}
	ll a=n*vy,b=m*vx,c=x*vy-y*vx;
	ll d=exgcd(a,b),p1=b/d,p2=a/d,k=c/d;
	
	if(c%d)printf("NO\n");
	else
	{
		printf("YES\n");
		X*=k;Y*=k;X=(X%p1+p1)%p1;if(!X)X+=p1;Y=(c-a*X)/b;
		while(Y>0)X+=p1,Y-=p2;
		writeln(X-Y-2); 
	}
}
int main()
{
	ll t=read();
	for(ll i=1;i<=t;i++)work();
	return 0;
} 
posted @ 2021-05-27 17:31  Creed-qwq  阅读(52)  评论(0编辑  收藏  举报