题目描述
难度分:1900
输入n(1≤n≤2×105),q(1≤q≤2×105)和长为 n的字符串s,只包含UDLR
。
一开始,你在平面直角坐标系的原点(0,0),你需要从左到右执行s上的操作,UDLR
分别表示向上/下/左/右移动一个单位。
然后输入q个询问,每个询问输入x、y(−n≤x,y≤n)、l、 r(1≤l≤r≤n)。
你需要把原串s的下标从l到r这一段reverse。(下标从1开始)然后依旧从 (0,0)出发,从左到右执行s上的操作,判断路径是否包含(x,y)。输出YES
或NO
。
注意询问是互相独立的,每次询问修改的都是原串s。
输入样例1
8 3
RDLLUURU
-1 2 1 7
0 0 3 4
0 1 7 8
输出样例1
YES
YES
NO
输入样例2
4 2
RLDU
0 0 2 2
-1 -1 2 3
输出样例2
YES
NO
输入样例3
10 6
DLUDLRULLD
-1 0 1 10
-1 -2 2 5
-4 -2 6 10
-1 0 3 9
0 1 4 7
-3 -1 5 8
输出样例3
YES
YES
YES
NO
YES
YES
算法
前缀和+二分查找
构建一个二元组数组idx2pos,idx2pos[i]表示执行完指令i时的横纵坐标。构建一个哈希映射pos2idx,pos2idx[(x,y)]表示处于(x,y)位置时的指令编号列表。
分析一下可以发现,对于某条询问(x,y,l,r),如果(x,y)最早出现的指令编号<l,或>r,反转子串[l,r]就没有任何影响,直接输出YES
。否则(x,y)出现时就应该在子串[l,r]内部,如果要求在执行这一段指令时经过了(x,y)这个位置,假设是指令i∈[l,r]时经过的,反转[l,r]之后就相当于先执行指令[1,l−1],再反着执行指令[i,r]。而执行某一段区间的指令时对x和y的增量dx和dy并不会因为反转了指令序列而发生改变,因此根据前缀和的思想,执行[i,r]指令带来的两个维度的增量分别为
dx=idx2pos[r].x−idx2pos[i−1].x
dy=idx2pos[r].y−idx2pos[i−1].y
而我们要求
idx2pos[l−1].x+dx=x
idx2pos[l−1].y+dy=y
所以
nx=idx2pos[i−1].x=idx2pos[l−1].x+idx2pos[r].x−x
ny=idx2pos[i−1].y=idx2pos[l−1].y+idx2pos[r].y−y。
最后二分查找pos2idx[(nx,ny)]中大于等于l−1的第一个指令是不是<r(因为i∈[l,r],所以i−1∈[l−1,r)),是就说明反转完[l,r]会经过(x,y),否则不经过。
复杂度分析
时间复杂度
实现的时候没有重现哈希函数,pos2idx使用的有序表,但这里还是按理论的时间复杂度来分析。预处理得到idx2pos和pos2idx只需要遍历一遍字符串s,时间复杂度是线性的,为O(n)。接下来在线处理每条询问,在最差情况下需要对每条询问都进行时间复杂度为O(log2n)的二分查找,因此时间复杂度为O(qlog2n)。
综上,整个算法的时间复杂度为O(n+qlog2n)。
空间复杂度
除了输入的字符串s,使用了映射pos2idx,其中最多存O(n)级别的指令。还使用了idx2pos数组,它记录了原本的n条指令执行完时的位置,也是O(n)的。因此,整个算法的额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int n, q;
char s[N];
int main() {
scanf("%d%d", &n, &q);
scanf("%s", s + 1);
vector<array<int, 2>> idx2pos(n + 1);
map<array<int, 2>, vector<int>> pos2idx;
int x = 0, y = 0;
pos2idx[{x, y}].push_back(0);
idx2pos[0] = {x, y};
for(int i = 1; i <= n; i++) {
if(s[i] == 'L') x--;
else if(s[i] == 'R') x++;
else if(s[i] == 'U') y++;
else y--;
pos2idx[{x, y}].push_back(i);
idx2pos[i] = {x, y};
}
for(int i = 1; i <= q; i++) {
int x, y, l, r;
scanf("%d%d%d%d", &x, &y, &l, &r);
if(pos2idx.count({x, y}) && pos2idx[{x, y}][0] < l) {
puts("YES");
continue;
}
if(pos2idx.count({x, y}) && pos2idx[{x, y}].back() > r) {
puts("YES");
continue;
}
int nx = idx2pos[l - 1][0] + idx2pos[r][0] - x, ny = idx2pos[l - 1][1] + idx2pos[r][1] - y;
if(pos2idx.count({nx, ny})) {
auto& v = pos2idx[{nx, ny}];
auto it = lower_bound(v.begin(), v.end(), l - 1);
if(it != v.end() && *it <= r) {
puts("YES");
}else {
puts("NO");
}
}else {
puts("NO");
}
}
return 0;
}