题意:从(0,0)沿(1,1)射出来一个东西,在封闭的(n,m)(n,m<1e5)矩形内撞来撞去,k(k<1e5)次询问(a,b),问到达(a,b)的最小时间
题解:第一感觉,肯定是把该平面展开,然后在所有展开的平面中对应的点,满足最小的dx == dy的就是解
对于横坐标,所有展开后的点的横坐标有可能是a,2*n-a,2*n+a。。。公式2*x*n ± a
纵坐标类似2*y*m ± b
要撞到,满足2*x*n ± a == 2*y*m ± b,最小的即为解
也就是说2*x*n - 2*y*m == ±(a ± b)
扩展欧几里得
LL n, m;
LL extend_Euclid(LL a, LL b, LL &x, LL &y){
if(b==0){
x = 1; y = 0;
return a;
}
LL r = extend_Euclid(b, a%b, y, x);
y -= a/b*x;
return r;
}
///ax + by = c
LL equation(LL a, LL b, LL c, LL &x, LL &y)
{
LL g = extend_Euclid(a, b, x, y);
if(c % g) return -1;
LL ran = b / g;
x *= c/g;
if(ran < 0) ran = -ran;
x = (x%ran + ran) % ran;
return 0;
}
LL work(LL dx, LL dy, LL M) {
LL k, s;
if(equation(2*n, -2*m, -dx+dy, k, s) == -1)
return M + 1;
LL temp = 2 * k * n + dx;
if(temp < 0 || temp > M) return M + 1;
return temp;
}
LL solve(LL x, LL y) {
LL g = __gcd(n, m);
///max1肯定是满足的解
LL max1 = 1LL * m / g * n;
LL ans = max1 + 1;
ans = min(ans, work(-x, -y, max1));
ans = min(ans, work(-x, y, max1));
ans = min(ans, work(x, -y, max1));
ans = min(ans, work(x, y, max1));
if(ans == max1 + 1) return -1;
return ans;
}
int main() {
int k;
while(~scanf("%I64d%I64d%d", &n, &m, &k)) {
for(int i = 0;i < k;i++) {
LL x, y;
scanf("%I64d%I64d", &x, &y);
printf("%I64d\n", solve(x, y));
}
}
return 0;
}
当然,还可以用别的方法,超级无敌大暴力
先求出到达边上每个点的最短时间(注意方向问题)
对于每个点,只要求出从四个方向来的时间,找最小就可以了
暴力时间比扩展欧几里得快。。
int a[MAX],b[MAX];
LL vis[MAX][4][4];
int main(){
int n,m,k;
while(cin>>n>>m>>k){
if(n==0 && m==0)
break;
for(int i=0;i<k;i++){
scanf("%d",a+i);
scanf("%d",b+i);
}
mem(vis,-1);
int tmp=0;
int x=0,y=0;
LL ti=0;
while(true){
if(tmp==0){
if((n-x)+y==m) break;
if((n-x)+y<m){
vis[(n-x)+y][3][0]=ti+n-x;
ti=ti+n-x;
y=(n-x)+y;
x=n;
tmp=3;
}
else{
vis[x+(m-y)][2][0]=ti+m-y;
ti=ti+m-y;
x=x+(m-y);
y=m;
tmp=1;
}
}
else if(tmp==1){
if(y-(n-x)==0) break;
if(y>n-x){
vis[y-(n-x)][3][1]=ti+(n-x);
ti=ti+(n-x);
y=y-(n-x);
x=n;
tmp=2;
}
else{
vis[x+y][0][1]=ti+y;
ti=ti+y;
x=x+y;
y=0;
tmp=0;
}
}
else if(tmp==2){
if(x==y) break;
if(x<y){
vis[y-x][1][2]=ti+x;
ti=ti+x;
y=y-x;
x=0;
tmp=1;
}
else{
vis[x-y][0][2]=ti+y;
ti=ti+y;
x=x-y;
y=0;
tmp=3;
}
}
else if(tmp==3){
if(y+x==m) break;
if(y+x<m){
vis[y+x][1][3]=ti+x;
ti=ti+x;
y=y+x;
x=0;
tmp=0;
}
else{
vis[x-(m-y)][2][3]=ti+(m-y);
ti=ti+(m-y);
x=x-(m-y);
y=m;
tmp=2;
}
}
}
for(int i=0;i<k;i++){
LL ans=1e18;
if(a[i]==b[i]){
printf("%d\n",a[i]);
continue;
}
if((n-a[i])+b[i]>m){
if(vis[a[i]+(m-b[i])][2][3]!=-1){
ans=min(ans,vis[a[i]+(m-b[i])][2][3]+m-b[i]);
}
}
if((n-a[i])+b[i]<m){
if(vis[b[i]+n-a[i]][3][1]!=-1){
ans=min(ans,vis[b[i]+n-a[i]][3][1]+n-a[i]);
}
}
if(b[i]>n-a[i]){
if(vis[b[i]-(n-a[i])][3][0]!=-1){
ans=min(ans,vis[b[i]-(n-a[i])][3][0]+n-a[i]);
}
}
if(b[i]<n-a[i]){
if(vis[a[i]+b[i]][0][2]!=-1){
ans=min(ans,vis[a[i]+b[i]][0][2]+b[i]);
}
}
if(a[i]<b[i]){
if(vis[b[i]-a[i]][1][3]!=-1){
ans=min(ans,vis[b[i]-a[i]][1][3]+a[i]);
}
}
if(a[i]>b[i]){
if(vis[a[i]-b[i]][0][1]!=-1){
ans=min(ans,vis[a[i]-b[i]][0][1]+b[i]);
}
}
if(a[i]+b[i]<m){
if(vis[a[i]+b[i]][1][2]!=-1){
ans=min(ans,vis[a[i]+b[i]][1][2]+a[i]);
}
}
if(a[i]+b[i]>m){
if(vis[a[i]-(m-b[i])][2][0]!=-1){
ans=min(ans,vis[a[i]-(m-b[i])][2][0]+(m-b[i]));
}
}
if(ans!=1e18) printf("%I64d\n",ans);
else printf("-1\n");
}
}
return 0;
}