题解:AT_abc416_e [ABC416E] Development
Chen78999
·
·
题解
题意简述
给定一张无向图,有 N 个点和 M 条边,每条边都有一个正的代价。此外,还有 K 个点有机场,可以用 T 的代价从任何一个有机场的点到另一个有机场的点。现在有三种操作:
- 加边。
- 在指定点上建设机场。
- 求所有可达点对的最少代价之和。
我们需要处理所有 Q 个操作,并输出所有第三种操作的结果。
朴素做法
如果我们每遇到一次 3 操作就用 Floyd 求一次全源最短路,那么最坏复杂度为 {\color{red}O(N^3Q)},代入题目中变量 N \le 500,Q \le 1000 后,计算得 N^3Q = 1.25 \times {10}^{11},显然无法通过。
所以我们必须考虑优化。
优化
我们可以先考虑操作较少的情况。
没有加边操作和机场的情况
这时,我们可以预先用 Floyd 求一遍最短路并求和,然后可以 O(1) 回答每个询问,时间复杂度为 O(N^3+Q)。
有加边操作的情况
同样地,我们可以预先用 Floyd 求一遍所有点对间的最短路。
当 x 和 y 之间添加了一条代价为 t 的边时,i 和 j 之间的最少代价就会变成以下其中之一:
- 从 i 出发到达 x,然后通过新边到达 y,最后到达 j 的代价;
- 从 i 出发到达 y,再经过新边到达 x,然后到达 j 的代价;
- 保持不变。
所以此时我们可以先用 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;
}
```