1 solutions

  • 1
    @ 2025-5-31 22:59:27

    本题是一个经典的优化建图问题,线段树优化建图、利用贪心性质分析之后然后 dp 都可以,这里只介绍一种基于非负最短路特性的隐式松弛做法。

    对于非负最短路,想要构成其最短路径树有一个关键的性质:每一个节点至多被松弛 1 次。

    因此我们可以使用 set 或者区间染色并查集等数据结构,保存集合内尚未松弛的点。而从队列当中拿出一个已经松弛过的点之后,遍历其邻接且未松弛的点,将他们的最短距离确定,并加入队列即可。一个点遍历完之后,在集合中删掉这些松弛点,这样可以保证每一个点都只被遍历了 1 次。

    总复杂度 O(nlogn)O(n\log n),即便是 python3 也能轻松通过。

    #include <stdio.h>
    #include <string.h>
    #include <set>
    #include <queue>
    #include <algorithm>
    inline int rd()
    {
        int k = 0, f = 1;
        char s = getchar();
        while (s < '0' || s > '9') f = (s == '-') ? 0 : f, s = getchar();
        while (s >= '0' && s <= '9') k = (k << 1) + (k << 3) + (s ^ '0'), s = getchar();
        return f ? k : -k;
    }
    inline void wr(int x)
    {
        if (x < 0) putchar('-'), x = -x;
        if (x > 9) wr(x / 10);
        putchar((x % 10) ^ '0');
    }
    const int N = 100010;
    int n;
    int a[N], b[N], f[N];
    bool vis[N];
    std::set<int> s;
    std::queue<int> q;
    int main()
    {
        n = rd();
        for (int i = 1; i <= n; ++i) a[i] = i - rd(), s.insert(i);
        for (int i = 1; i <= n; ++i) b[i] = rd();
        memset(f, 0xff, (n + 1) << 2);
    
        q.push(1), f[1] = 0, s.erase(1), vis[1] = 1;
        while (q.size())
        {
            int u = q.front();
            q.pop();
            auto st = s.lower_bound(std::min(n, u + 1)), cur = st, ed = s.lower_bound(std::min(n + 1, u + b[u] + 1));
            while (cur != ed)
            {
                int v = a[*cur];
                if (!vis[v]) f[v] = f[u] + 1, vis[v] = 1, q.push(v);
                ++cur;
            }
            s.erase(st, ed);
        }
        wr(f[n]);
    }
    
    • @ 2025-6-1 17:01:13

      很简洁的思路!

      我线段树优化建图写了一堆,最后还全WA了/kk学习了/bx

  • 1

Information

ID
43
Time
500ms
Memory
512MiB
Difficulty
3
Tags
# Submissions
41
Accepted
7
Uploaded By