1 条题解

  • 0
    @ 2025-8-24 22:22:03

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Tweetuzki
    AFO

    搬运于2025-08-24 22:22:03,当前版本为作者最后更新于2020-05-27 21:13:21,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    给出一张 nn 个点,mm 条边的有向图,qq 次询问任意两点间最短路。

    给出一常数 kk,保证每条边 (u,v)(u, v) 保证 $\left\lfloor \frac{u}{k} \right\rfloor + 1 = \left\lfloor \frac{v}{k} \right\rfloor$。

    n50 000n \le 50\ 000q10 000q \le 10\ 000k5k \le 5

    感觉 NOIP2018 普及了动态 DP 以后,这题变简单了。

    对于每个块维护一个矩阵做动态 DP。

    设进入这个块时,从起点到每个点的距离依次为

    $$\begin{bmatrix} f_0 & f_1 & \cdots & f_{k - 1} \end{bmatrix} $$

    出完这个块之后,需要从这个块内的边走出去,更新矩阵变成

    $$\begin{bmatrix} g_0 & g_1 & \cdots & g_{k - 1} \end{bmatrix} $$

    首先有一个暴力 DP:

    $$g_j = \min\limits_{i = 0} ^ {k - 1} \left(f_i + w_{i, j}\right) $$

    其中 i,ji, j 表示该块内的 ii 号点连向下一个块内 jj 号点的边的边权,没有设为 ++\infty

    学习了动态 DP 之后,知道这个东西是可以写成矩阵 “乘法” 形式的,转移矩阵是:

    $$\begin{bmatrix} w_{0, 0} & w_{0, 1} & \cdots & w_{0, k - 1} \\ w_{1, 0} & w_{1, 1} & \cdots & w_{1, k - 1} \\ \vdots & \vdots & \ddots & \vdots \\ w_{k - 1, 0} & w_{k - 1, 1} & \cdots & w_{k - 1, k - 1} \end{bmatrix} $$

    这个东西是 min\min++ 套起来,是有结合律的。所以倍增或线段树维护一下区间连乘积,每次查询的时候直接乘即可。

    经过一些细节优化可以做到,时间复杂度 O((n+q)k2logn)\mathcal{O}((n + q) k ^ 2 \log n)

    代码:

    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    
    const int MaxN = 50000, MaxK = 5;
    const int MaxLog = 16;
    const int INF = 0x3F3F3F3F;
    
    int N, M, K, Q;
    
    struct matrix_t {
      int mat[MaxK][MaxK];
      matrix_t() { memset(mat, 0x3F, sizeof mat); }
    
      inline friend matrix_t operator*(const matrix_t &a, const matrix_t &b) {
        matrix_t c;
        for (int i = 0; i < K; ++i)
          for (int j = 0; j < K; ++j) {
            int x = INF;
            for (int k = 0; k < K; ++k)
              x = std::min(x, a.mat[i][k] + b.mat[k][j]);
            c.mat[i][j] = x;
          }
        return c;
      }
    };
    
    struct vector_t {
      int mat[MaxK];
      vector_t() { memset(mat, 0x3F, sizeof mat); }
    
      inline friend vector_t operator*(const vector_t &a, const matrix_t &b) {
        vector_t c;
        for (int i = 0; i < K; ++i)
          for (int j = 0; j < K; ++j)
            c.mat[j] = std::min(c.mat[j], a.mat[i] + b.mat[i][j]);
        return c;
      }
    };
    
    matrix_t F[MaxLog + 1][MaxN + 5];
    
    void init() {
      scanf("%d %d %d %d", &K, &N, &M, &Q);
      for (int i = 1; i <= M; ++i) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        F[0][u / K].mat[u % K][v % K] = std::min(F[0][u / K].mat[u % K][v % K], w);
      }
    }
    
    inline void query(vector_t &f, int l, int r) {
      int x = l;
      for (int i = MaxLog; i >= 0; --i)
        if ((r - l + 1) & (1 << i)) {
          f = f * F[i][x];
          x += (1 << i);
        }
    }
    
    void solve() {
      for (int i = 1; (1 << i) <= N / K + 1; ++i)
        for (int j = 0; j + (1 << i) - 1 < (N / K + 1); ++j)
          F[i][j] = F[i - 1][j] * F[i - 1][j + (1 << (i - 1))];
      for (int q = 1; q <= Q; ++q) {
        int a, b;
        scanf("%d %d", &a, &b);
        if (a / K == b / K) puts("-1");
        else {
          vector_t f;
          f.mat[a % K] = 0;
          query(f, a / K, b / K - 1);
          if (f.mat[b % K] == INF) puts("-1");
          else printf("%d\n", f.mat[b % K]);
        }
      }
    }
    
    int main() {
      init();
      solve();
      return 0;
    }
    
    • 1

    信息

    ID
    5595
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者