有一个二维平面,上面有 \(n\) 条横线和竖线,有 \(q\) 个机器人,初始位置给定,每秒沿原来的方向移动 \(1\) 单位,遇到交叉点当前时间是偶数就转到竖线,否则转到横线,求每个机器人 \(d_i\) 秒后的位置。
暴力做法就是对每个机器人模拟,复杂度显然爆炸了,我们研究一下怎么快速跳过去。假设机器人起点是 \((x_0,y_0)\),走到了 \((X,Y)\) 这个岔口,这段路的时间 \(t\) 满足 t=X-x_0+Y-y_0 即曼哈顿距离(这或许是这个题叫"漫步曼哈顿"的原因)。根据题意转向与否只与 \(t\) 有关,那么我们定义机器人的特征值 \(P=(x_0+y_0)\bmod 2\),将 \(t\) 代入得到 t=(X+Y)-(x_0+y_0)\\equiv X+Y-P(\\bmod 2) 这说明机器人在路口时时间由路口坐标和 \(X+Y\) 以及它本身的特征值决定。那么转向规则就变成了:
- 转成竖线:\(t\equiv 0(\bmod 2)\Longrightarrow X+Y\equiv P(\bmod 2)\)
- 转成横线:\(t\equiv 1(\bmod 2)\Longrightarrow X+Y\not\equiv P(\bmod 2)\)
下一步非常关键,假设现在在 \(X_k\) 的竖线上,路口横线为 \(Y_k\),如果转向必须满足 X_k+Y_k\\not\\equiv P(\\bmod 2) 接着在 \(Y_k\) 的横线上,下一个路口竖线是 \(X_{k+1}\),如果转向必须满足 X_{k+1}+Y_k\\equiv P(\\bmod 2) 联立后可消掉 \(P\) 和 \(Y_k\) 得到 X_k\\not\\equiv X_{k+1}(\\bmod 2) 同理可得 \(Y_k\not\equiv Y_{k+1}(\bmod 2)\)。这说明只要转弯,道路编号就会翻转,不停地交替下去。
基于这个性质,如果机器人目前在 \(X_i\) 的岔路转弯了,那么下一次重新回到竖线方向一定在第一个与 \(X_i\) 奇偶性不同的 \(X_j\) 上。这相当于把一个二维平面转成横竖线两种独立的模式。换句话说,从 \(X_k\) 出发,下一次转回来的点只依赖于 \(X_k\)。所以我们可以使用倍增来加速模拟的过程。
倍增预处理要开两个数组,因为横竖是独立的。\(f_{i,0}\) 表示从 \(i\) 出发第一条奇偶性不同的路,这里可以 \(O(n)\) 算一下。后面就是经典的转移。而查询每个机器人的位置分为三部分:
- 首先,机器人不一定一开始就在路口,先二分到第一个路口。如果 \(d_i\) 都不够走到路口直接返回
- 接下来最关键的,从大到小枚举试跳,更新坐标,不够了也直接返回
- 最后还剩一段,这里不能再拐两次了,最多拐一次弯,分类讨论一下
最终时间复杂度 \(O((n+q)\log n)\)。
给一下实现,感觉还是很清晰(没有注释)。
cpp
#include<bits/stdc++.h>
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)
using namespace std;
typedef long long i64;
typedef __int128 i128;
const int N=2e5+5,LOG=20;
void solve();
int n,q;
int fx[N][LOG],fy[N][LOG];
vector<int> X,Y;
signed main(){
int Test=1;
// scanf("%d",&Test);
while(Test--) solve();
return 0;
}
void build(vector<int>& a,int f[][LOG]){
int len=a.size();
L(i,0,LOG-1,1) f[len][i]=len;
R(i,len-1,0,1){
if((a[i]%2)!=(a[i+1]%2)) f[i][0]=i+1;
else f[i][0]=f[i+1][0];
L(j,1,LOG-1,1) f[i][j]=f[f[i][j-1]][j-1];
}
}
pair<int,int> query(int x,int y,int d){
int xi=lower_bound(X.begin(),X.end(),y)-X.begin(),
yi=lower_bound(Y.begin(),Y.end(),x)-Y.begin();
int t=0;
if(y<X[xi]){
int dist=X[xi]-y;
if(dist>d) return {x,y+d};
d-=dist;
t+=dist;
y=X[xi];
}
if(x<Y[yi]){
int dist=Y[yi]-x;
if(dist>d) return {x+d,y};
d-=dist;
t+=dist;
x=Y[yi];
}
R(j,LOG-1,0,1){
int nx=fx[xi][j],ny=fy[yi][j];
if(nx==X.size()||ny==Y.size()) continue;
int dx=Y[ny]-Y[yi],dy=X[nx]-X[xi];
if(dx>d||dy>d) continue;
int dist=dx+dy;
if(dist>d) continue;
d-=dist;
t+=dist;
xi=nx,yi=ny;
x=Y[yi],y=X[xi];
}
if(t%2){
int ny=fy[yi][0];
if(ny==Y.size()) return {x+d,y};
int dx=Y[ny]-Y[yi];
if(dx>=d) return {x+d,y};
d-=dx;
x=Y[ny];
return {x,y+d};
}
else{
int nx=fx[xi][0];
if(nx==X.size()) return {x,y+d};
int dy=X[nx]-X[xi];
if(dy>=d) return {x,y+d};
d-=dy;
y=X[nx];
return {x+d,y};
}
}
void solve(){
scanf("%d%d",&n,&q);
L(i,0,n-1,1){
char t[3];
int c;
scanf("%s%d",&t,&c);
if(t[0]=='H') X.push_back(c);
else Y.push_back(c);
}
sort(X.begin(),X.end());
sort(Y.begin(),Y.end());
X.push_back(2e9);
Y.push_back(2e9);
build(X,fx);
build(Y,fy);
while(q--){
int x,y,d;
scanf("%d%d%d",&x,&y,&d);
auto [ansx,ansy]=query(x,y,d);
printf("%d %d\n",ansx,ansy);
}
}