公告 & Solution - 公路旅行

发布时间 2023-11-14 20:00:19作者: liuzimingc

以后应该会用 Obsidian 搭个博客,博客园可能会被弃用了。

为了有点价值放个不知道什么东西上来。


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