Solution - 公路旅行
Link。
又名:《不会 T1 记》。
原来用到了神秘的倍增!但是我写了一个申必二分,最坏 \(O(qn \log n)\),甚至不如暴力,我是🤡。
类似于 ST 表那样,\(f_{i, j}\) 表示从 \(i\) 出发经过 \(2 ^ j\) 段不大于 \(L\) 的路程,那么 \(f_{i, 0}\) 显然是单调的,维护个指针就行了,状态转移方程 \(f_{i, j} = f_{f_{i, j - 1}, j - 1}\)。
然后跳倍增的时候类似于 LCA,跳的是二的幂次。然后注意跳不能是 \(\leq b\),因为你不知道最后跳到了没有,只能写 \(< b\),然后因为我们是会最后只跳 \(1\) 步的,所以再跳 \(1\) 步就一定 \(\geq b\),答案加个一就行。跟 LCA 不能直接跳到相同而是只能跳到各自祖先相同原理是一样的。
好像是马蜂大改后第一次粘代码?
namespace liuzimingc {
const int N = 1e5 + 5;
#define endl '\n'
int n, x[N], L, q, a, b, go[N][21];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> x[i];
cin >> L >> q;
for (int i = 1, j = 1; i <= n; i++) {
while (j + 1 <= n && x[j + 1] - x[i] <= L) j++;
go[i][0] = j;
}
for (int j = 1; j <= 20; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
go[i][j] = go[go[i][j - 1]][j - 1];
while (q--) {
cin >> a >> b;
if (a > b) swap(a, b);
int ans = 0;
for (int i = 20; ~i; i--)
if (go[a][i] && go[a][i] < b) a = go[a][i], ans += 1 << i;
ans++;
cout << ans << endl;
}
return 0;
}
} // namespace liuzimingc
Posted by liuzimingc