题解:AT_abc416_e [ABC416E] Development

· · 题解

题意简述

给定一张无向图,有 N 个点和 M 条边,每条边都有一个正的代价。此外,还有 K 个点有机场,可以用 T 的代价从任何一个有机场的点到另一个有机场的点。现在有三种操作:

  1. 加边。
  2. 在指定点上建设机场。
  3. 求所有可达点对的最少代价之和。

我们需要处理所有 Q 个操作,并输出所有第三种操作的结果。

朴素做法

如果我们每遇到一次 3 操作就用 Floyd 求一次全源最短路,那么最坏复杂度为 {\color{red}O(N^3Q)},代入题目中变量 N \le 500Q \le 1000 后,计算得 N^3Q = 1.25 \times {10}^{11},显然无法通过。

所以我们必须考虑优化。

优化

我们可以先考虑操作较少的情况。

没有加边操作和机场的情况

这时,我们可以预先用 Floyd 求一遍最短路并求和,然后可以 O(1) 回答每个询问,时间复杂度为 O(N^3+Q)

有加边操作的情况

同样地,我们可以预先用 Floyd 求一遍所有点对间的最短路。

xy 之间添加了一条代价为 t 的边时,ij 之间的最少代价就会变成以下其中之一:

所以此时我们可以先用 O(N^3) 预处理,然后 O(N^2) 处理每个询问,时间复杂度为 O(N^3+N^2Q)

有加边操作和机场的情况

如果每个城市都有机场,那么最多可以得到 N^2 条边。如果每建一个机场,就在所有有机场的城镇之间添加边,并重新计算最少代价,总共需要 O(N^2) 时间,总时间复杂度将达到 O({\color{red}N^4}+N^2Q),无法通过。即使等到操作 3 再执行 Floyd 算法,情况也是一样。

正解

我们注意到,可以维护每个节点到最近机场的最少代价,第 i 号节点到最近机场的最少代价记作 A_i。如果从第 i 号节点无法到达任何一个机场,则 A_i = +\infty

这样对每个加机场的操作,我们只需要 O(N) 更新每个点到最近有机场城市的最少代价即可。

:::::warning[注意]{open} 对于每个加边操作,加边完成后也要更新每个点到最近有机场城市的最少代价。对应代码95-102行。

::::info[例子] 考虑以下情况:

一共有 4 个顶点,从第一个点到第二个点代价为 1,从第二个顶点到第三个顶点代价为 100

有以下两个操作: 1. 添加一条连接点 $1$ 和 $3$ 的边,代价为 $2$。 2. 求所有可达点对的最少代价之和。 如果在加边完成后不更新每个点到最近有机场城市的最少代价,则 $A_1$ 仍为 $101$ (应该为 $2$),**在计算节点 $1$ 到节点 $4$ 的代价时会发生错误**。 :::: ::::: 这时,从第 $x$ 个节点到第 $y$ 个节点的代价可表示为以下两者的最小值: - 只经过公路的代价。 - 先到最近的机场,坐飞机到另一个机场后再到第 $y$ 个节点的总代价,即 $A_x + T + A_y$。 这样,我们可以先 $O(N^3)$ 预处理所有点对间最短路径,$O(N^2)$ 处理第一种和第三种操作,$O(N)$ 处理第二种操作,总时间复杂度为 $O(N^3+N^2Q)$。 ## Code ```c++ line-numbers lines=95-102 #include <bits/stdc++.h> using namespace std; using uint = unsigned int; using ll = long long; using ull = unsigned long long; using vi = vector<int>; using pii = pair<int, int>; template<typename I> inline bool in_range(I num, I minv, I maxv) { return num >= minv && num <= maxv; } template<typename I> inline bool out_of_range(I num, I minv, I maxv) { return !in_range(num, minv, maxv); } #define rep(index,start,end) for(decltype((start)+0) index=(start),__=(end);(index)<=__;++index) #define all(x) x.begin(),x.end() template<typename T> T & chmax(T & a, const T & b) { if (b > a) a = b; return a; } template<typename T> T & chmin(T & a, const T & b) { if (b < a) a = b; return a; } int n,m,k,T; vector<pii> edges[510]; ll d[510][510], e[510][510], a[510]; bool airport[510], exists[510][510]; ll dist[510][510]; set<int> hasairport; void setup() { } void Floyd() { // Floyd 算法 rep(k,1,n) rep(i,1,n) rep(j,1,n) { chmin(d[i][j], d[i][k] + d[k][j]); } } void solve(int testcase = 0) { cin>>n>>m; rep(i,1,n) rep(j,1,n) d[i][j] = 0x3f3f3f3f3f3f3f3fll; rep(i,1,n) d[i][i] = 0; rep(i,1,m) { int u,v; ll w; cin>>u>>v>>w; chmin(d[u][v],w); chmin(d[v][u],w); } Floyd(); cin>>k>>T; int airver = 1; memset(a, 0x3f, sizeof(a)); rep(j,1,k) { int x; cin>>x; airport[x] = true; hasairport.insert(x); rep(i,1,n) chmin(a[i], d[i][x]); } int q,ver = 1, nowver = 0, nowairver = 0; ll ans = 0; cin>>q; while (q--) { int mode; cin>>mode; if (mode == 1) { int u,v; ll w; cin>>u>>v>>w; chmin(d[u][v],w); chmin(d[v][u],w); ver++; rep(i,1,n) rep(j,1,n) { if (d[i][u] < 0x3f3f3f3f3f3f3f3fll){ chmin(d[i][j], d[i][u] + w + d[v][j]); chmin(d[i][j], d[i][v] + w + d[u][j]); } } rep(i,1,n) { // 这是一个坑! a[i] = 0x3f3f3f3f3f3f3f3fll; if (airport[i]) a[i] = 0; for (auto j: hasairport) { if (i == j) continue; chmin(a[i], d[i][j]); } } } else if (mode == 2) { int x; cin >> x; airport[x] = true; hasairport.insert(x); ver++; airver ++; a[x] = 0; rep(i,1,n) { chmin(a[i], d[i][x]); } } else { // mode = 3 ll ans = 0; rep(i,1,n) rep(j,1,n) { ll tmp = min(d[i][j], a[i]+T+a[j]); if (tmp < 0x3f3f3f3f3f3f3f3fll) { ans += tmp; } // cout<<tmp<<" \n"[j==n]; } cout<<ans<<endl; } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); setup(); int T = 1; // cin >> T; rep(testcase, 1, T) { solve(testcase); } return 0; } ```