专栏文章
题解:AT_abc416_e [ABC416E] Development
AT_abc416_e题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miopbi1c
- 此快照首次捕获于
- 2025/12/02 22:56 3 个月前
- 此快照最后确认于
- 2025/12/02 22:56 3 个月前
题意简述
给定一张无向图,有 个点和 条边,每条边都有一个正的代价。此外,还有 个点有机场,可以用 的代价从任何一个有机场的点到另一个有机场的点。现在有三种操作:
- 加边。
- 在指定点上建设机场。
- 求所有可达点对的最少代价之和。
我们需要处理所有 个操作,并输出所有第三种操作的结果。
朴素做法
如果我们每遇到一次 3 操作就用 Floyd 求一次全源最短路,那么最坏复杂度为 ,代入题目中变量 , 后,计算得 ,显然无法通过。
所以我们必须考虑优化。
优化
我们可以先考虑操作较少的情况。
没有加边操作和机场的情况
这时,我们可以预先用 Floyd 求一遍最短路并求和,然后可以 回答每个询问,时间复杂度为 。
有加边操作的情况
同样地,我们可以预先用 Floyd 求一遍所有点对间的最短路。
当 和 之间添加了一条代价为 的边时, 和 之间的最少代价就会变成以下其中之一:
- 从 出发到达 ,然后通过新边到达 ,最后到达 的代价;
- 从 出发到达 ,再经过新边到达 ,然后到达 的代价;
- 保持不变。
所以此时我们可以先用 预处理,然后 处理每个询问,时间复杂度为 。
有加边操作和机场的情况
如果每个城市都有机场,那么最多可以得到 条边。如果每建一个机场,就在所有有机场的城镇之间添加边,并重新计算最少代价,总共需要 时间,总时间复杂度将达到 ,无法通过。即使等到操作 3 再执行 Floyd 算法,情况也是一样。
正解
我们注意到,可以维护每个节点到最近机场的最少代价,第 号节点到最近机场的最少代价记作 。如果从第 号节点无法到达任何一个机场,则 。
这样对每个加机场的操作,我们只需要 更新每个点到最近有机场城市的最少代价即可。
注意
对于每个加边操作,加边完成后也要更新每个点到最近有机场城市的最少代价。对应代码95-102行。
例子
考虑以下情况:
一共有 个顶点,从第一个点到第二个点代价为 ,从第二个顶点到第三个顶点代价为 。
,节点 有机场。
有以下两个操作:
- 添加一条连接点 和 的边,代价为 。
- 求所有可达点对的最少代价之和。
如果在加边完成后不更新每个点到最近有机场城市的最少代价,则 仍为 (应该为 ),在计算节点 到节点 的代价时会发生错误。
这时,从第 个节点到第 个节点的代价可表示为以下两者的最小值:
- 只经过公路的代价。
- 先到最近的机场,坐飞机到另一个机场后再到第 个节点的总代价,即 。
这样,我们可以先 预处理所有点对间最短路径, 处理第一种和第三种操作, 处理第二种操作,总时间复杂度为 。
Code
C#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;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...