题目大意:有 n n 个车站,每个车站有一个编号,第 种车只会经过编号 ≥i ≥ i 的站台。有 q q 次询问,每次询问从 号站台到 j j 号站台,在最优的乘车方案中,列车会停靠几次。
令 表示从 i i 号车站出发,经过 次停靠能够到达的最左侧的站点, r[k][i] r [ k ] [ i ] 表示从 i i 号车站出发,经过 次停靠能够到达的最右侧的站点。我们发现每个状态只可能从最左侧和最右侧的车站转移过来,则状态转移方程为:
对于每个询问,还是因为每个状态只可能从最左侧和最右侧的车站转移过来,所以按类似之前的方法倍增即可。时间复杂度 Θ((n+q)log2n) Θ ( ( n + q ) log 2 n ) 。详见代码。
#include <cstdio>
#include <iostream>
using namespace std;
const int logn = 17;
const int maxn = 100005;
int n, q, a[maxn], sz, st[maxn];
int l[maxn][logn], r[maxn][logn];
int main() {
scanf("%d %*d %d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
st[sz = 0] = 1;
for (int i = 1; i <= n; i++) {
while (sz && a[st[sz]] < a[i]) sz--;
l[i][0] = st[sz], st[++sz] = i;
}
st[sz = 0] = n;
for (int i = n; i >= 1; i--) {
while (sz && a[st[sz]] < a[i]) sz--;
r[i][0] = st[sz], st[++sz] = i;
}
for (int k = 0; k < 16; k++) {
for (int i = 1; i <= n; i++) {
l[i][k + 1] = min(l[l[i][k]][k], l[r[i][k]][k]);
r[i][k + 1] = max(r[r[i][k]][k], r[l[i][k]][k]);
}
}
int x, y, L, R, lb, rb, ans;
while (q--) {
scanf("%d %d", &x, &y);
if (x > y) {
swap(x, y);
}
ans = 0;
L = R = x;
for (int i = 16; i >= 0; i--) {
lb = min(l[L][i], l[R][i]);
rb = max(r[L][i], r[R][i]);
if (rb < y) {
L = lb, R = rb;
ans += 1 << i;
}
}
x = R;
L = R = y;
for (int i = 16; i >= 0; i--) {
lb = min(l[L][i], l[R][i]);
rb = max(r[L][i], r[R][i]);
if (lb > x) {
L = lb, R = rb;
ans += 1 << i;
}
}
printf("%d\n", ans);
}
return 0;
}