C. Ray Tracing——披着搜索外衣的扩展欧几里得
【题目大意】
给你一个n*m的矩形,光线从(0,0)出发,沿右上方向以每秒根号2米的速度运动,碰到矩形边界就会反弹(符合物理规律的反弹),询问k个点,这些点都在矩形内部且不在矩形边界上,求光经过这些点的最小时间。如果光不会经过这个点,输出-1.
【数据范围】
0<m,n,k<=100000
【吐槽】
最后1个小时都砸这道题上了,一直是搜索/模拟 的思路,然而正解是扩展欧几里得。(据说模拟也是可以的,然而我不会,sro_姬树流_orz)
【题解】
把矩形对称展开,最后小球在横纵坐标均为maxx=mn/gcd(m,n)处被吸收。
原坐标为(x,y)的小球经过轴对称展开,其坐标为(2kn±x,2sm±y),k,s为整数.要使得在吸收前经过点,则坐标必须在线段(0, 0)到(maxx, mxx)之间。
即要解方程2kn±x=2sm±y,求为正最小的2kn±x。利用扩展欧几里得解方程。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<ctime> 7 #include<algorithm> 8 using namespace std; 9 long long n,m,k,aa,bb,maxx; 10 inline long long read() 11 { 12 long long x=0,f=1; char ch=getchar(); 13 while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();} 14 while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} 15 return x*f; 16 } 17 long long exgcd(long long a,long long b,long long &x,long long &y) 18 { 19 if(b==0) {x=1; y=0; return a;} 20 long long r=exgcd(b,a%b,y,x); 21 y-=a/b*x; 22 return r; 23 } 24 long long gao(long long x,long long y) 25 { 26 long long c=x+y,dx,dy; 27 long long r=exgcd(aa,bb,dx,dy); 28 if(c%r) return maxx+1; 29 long long mo=bb/r; dx*=c/r; 30 if(mo<0) mo=-mo; 31 dx=(dx%mo+mo)%mo; 32 long long ans=2*n*dx-x; 33 if(ans<0||ans>maxx) return maxx+1; 34 return ans; 35 } 36 long long minn(long long a,long long b) {return a<b?a:b;} 37 long long solve(long long x,long long y) 38 { 39 long long r=__gcd(n,m); 40 maxx=n*m/r; 41 long long ans=maxx+1; 42 ans=minn(ans,gao(x,y)); 43 ans=minn(ans,gao(x,-y)); 44 ans=minn(ans,gao(-x,y)); 45 ans=minn(ans,gao(-x,-y)); 46 if(ans==maxx+1) return -1; 47 else return ans; 48 } 49 int main() 50 { 51 //freopen("cin.in","r",stdin); 52 //freopen("cout.out","w",stdout); 53 n=read(); m=read(); k=read(); 54 aa=2*n; bb=-2*m; 55 for(int i=1;i<=k;i++) 56 { 57 long long x=read(),y=read(); 58 printf("%I64d\n",solve(x,y)); 59 } 60 return 0; 61 }