Logo ryp 的博客

博客

U396561 删边最短路 题解

...
ryp
2025-12-01 12:50:16
She's not square

本文章由 WyOJ Shojo 从洛谷专栏拉取,原发布时间为 2024-01-10 20:20:15

首先将删边改为加边,问题就转化为:向最初的无边图中依次加入 $M$ 到 $1$ 条边,求加入后的最短路。

我们设 $f(i)$ 代表从点 $1$ 走到点 $i$ 的最短路径,有边界:$f(1) = 0$。在加入每一条边 $(u,v)$ 的过程中,影响的只有 $v$:$f(v) = \min\{f(v), f(u) + w\}$。

更新完点 $v$ 之后,我们更新可能被点 $v$ 影响到的每一个点,即做一次 BFS。

作为出题人,有以下注意点:

  • 本题不卡常,正确的算法应当都能通过,$O(m^2)$ 以上的暴力最短路不能通过。但是,STL 与手写可以差两倍时间。
  • 本题的数据:Subtask 0 是生成数据,Subtask 1 是手写数据,包括样例

以下是标程:

#include <iostream>
#include <vector>
#include <climits>
#include <cstring>
#define rep(v, s, e) for (auto v = (s); v < (e); v++)
#define rrep(v, e, s) for (auto v = (e); v >= (s); v--)
using namespace std;

const long long N = 2 * 1000, M = 100000, K = 10, wtsummax = 1e10;
long long f[N], q[M], pt[K], wi[M], qs[M * K];
vector<pair<int, long long>> e[N];
int ui[M], vi[M];

int
read (void)
{
  int res = 0, sign = 1;
  char c;

  while (!isdigit (c = getchar ()))
    if (c == '-') sign = -1;
  do res = res * 10 + c - '0'; while (isdigit (c = getchar ()));
  return res * sign;
}

void
spread (int s)
{
  int head = 0, tail = 1;
  q[0] = s;

  while (head <= tail)
    {
      int p = q[head++];

      for (auto t : e[p])
        {
          int v = t.first, w = t.second;
          if (f[v] > f[p] + w)
            f[v] = f[p] + w, q[tail++] = v;
        }
    }
}

int
main (void)
{
  int m, k, tail = 0;

  read (), m = read (), k = read ();
  rep (i, 0, k) pt[i] = read () - 1;
  rep (i, 0, m) ui[i] = read () - 1, vi[i] = read () - 1, wi[i] = read ();

  memset (f, 0x3f, sizeof f);
  f[0] = 0;

  rrep (i, m - 1, 0)
    {
      int u = ui[i], v = vi[i]; long long w = wi[i];

      e[u].push_back (make_pair (v, w));
      if (f[v] > f[u] + w)
        {
          f[v] = f[u] + w;
          spread (v);
        }

      rrep (j, k - 1, 0) qs[tail++] = f[pt[j]] > wtsummax ? INT_MAX : f[pt[j]];
    }

  tail--;
  while (tail >= 0)
    {
      rep (i, 0, k) printf ("%lld ", qs[tail--]);
      putchar ('\n');
    }

  return 0;
}

关于复杂度

这算法看起来是 $O(nm)$ 的,但卡不到,因为只有当前边更优的时候我们才会更新其他点,而且更新的点只是整个图很小的一部分。随机数据只到了大约二十到三十万的水平。

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。