CF1737D Ela and the Wiring Wizard 题解
题目链接:CF1737D Ela and the Wiring Wizard
题意:
Ela 需要通过机器网络,从机器 $1$ 发送一个大型包裹到机器 $n$。目前,由于网络条件不好,她抱怨网络太慢,包裹无法及时到达。幸运的是,一位电线向导向她伸出了援手,
据说叫cxy。网络可以用一个无向连通图表示,其中有 $n$ 个节点,每个节点代表一台机器。用 $m$ 条电线连接它们。第 $i$ 条电线用于连接机器 $u_i$ 和 $v_i$,并具有权重 $w_i$。如果大型包裹经过第 $i$ 条电线,则会在恰好 $w_i$ 微秒内从机器 $u_i$ 移动到机器 $v_i$(或反之亦然)。电线向导可以随意使用她的魔法,对于每个魔法,她将选择连接机器 $u_i$ 和 $v_i$ 的第 $i$ 条电线,并按照以下步骤重新布线:
- 选择一个与此电线连接的机器。不失一般性,假设我们选择 $v_i$。
- 选择一个当前连接到 $v_i$ 的机器(包括 $u_i$),称之为 $t_i$。断开编号为 $i$ 的电线与 $v_i$ 的连接,然后使用它连接 $u_i$ 和 $t_i$。
- 重布线电线 $i$ 需要 $w_i$ 微秒,且重布线后电线的权重不变。重布线后,某个机器可能会有一些电线连接到自己。此外,电线向导已经警告 Ela,重新布线可能会导致一些机器之间的临时断开连接,但 Ela 无论如何都忽略了这一点。她的任务是尽可能快地将大型包裹从机器 $1$ 送到机器 $n$。请注意,巫师可以在电线上使用她的魔法零次、一次或多次。为确保在传输大型包裹时网络无缝工作,一旦包裹从机器 $1$ 开始传输,电线向导就不能再使用她的魔法移动电线。
Ela 想知道,在电线向导的帮助下,将大型包裹从机器 $1$ 传输到 $n$ 所需的最短时间是多少。
输入格式:
每个测试包含多个测试用例。第一行包含测试用例的数量 $t$ ($1\le t\le 100$)。
测试用例的描述如下。
第一行包含 $n$ 和 $m$ ($2\le n\le 500$, $n-1\le m\le 250000$),节点数和边数。
接下来的 $m$ 行,第 $i$ 行包含 $u_i$、$v_i$ 和 $w_i$ ($1\le u_i,v_i\le n$, $1\le w_i\le 10^9$),表示第 $i$ 条边连接的两个节点和边的权重。
保证所有测试用例的 $n$ 总和不超过 $500$,$m$ 总和不超过 $250000$。每个测试用例中的图保证连通,无自环,但可能包含多重边。
输出格式:
对于每个测试用例,输出一个整数,表示从机器 $1$ 到 $n$ 传输大型包裹所需的最短时间。
简化题意:
有一个包含 $n$ 个点和 $m$ 条边的无向图,每条边 $i$ 连接着点 $u_i$ 和 $v_i$,权值为 $w_i$,通过这条边要用 $w_i$ 微秒。
Ela 需要从 $1$ 号点走到 $n$ 号点,但她觉得原本的路径太长了。好在,可爱的 cxy 可以帮她改变路径。
具体地,对于任意三点 $u,v,t$($u$ 和 $t$ 可以相同),若 $u$ 与 $v$ 之间有边 $i$,且 $v$ 与 $t$ 有边 $j$,那么她可以用 $w_i$ 微秒断开边 $i$,并且在 $u$ 与 $t$ 之间连一条权值为 $w_i$ 的边。她可以改任意条边,也可以不改。
Ela 想知道,她至少要花多久时间才能从 $1$ 号点走到 $n$ 号点(时间包括修改边的时间)。
有 $t$ 组数据。
$1\le t\le 100$
$2\le n\le 500,~n-1\le m\le 250000$
$1\le u_i,v_i\le n,~1\le w_i\le 10^9$
$\sum{n}\le 500,~\sum{m}\le 250000$
保证输入的图为联通图,无自环,但可能有重边。
比较好的一道题。一开始可能会想到用 Floyd 维护某些东西。
但是样例三其实暗示了我们,很有可能全程我们只用了一条边,并把它变成边 $(1,n)$ 。
考虑反证法证明:如果最优方案用了超过一条边,则这条路径上一定存在一条更短的边,直接用最短的即可。
那么问题就很简单了,因为怎么移动边与边权无关,所以我们先 Floyd 一下得到边权为 $1$ 的最短路。
然后枚举每条边 $(u,v)$ ,计算它变成 $(1,n)$ 的花费。转移如下:
如果 $(u,v)$ 恰好在 $1 \leadsto n$ 的某条路径上,则
否则考虑考虑让一个点走到中转点 $j$ ,然后把另一个点连向 $j$ (形成自环),接着直接移动就好了
时间复杂度 $\mathcal{O}(n^3 + nm)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x7f7f7f7f
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(515))
#define M ((int)(3e5 + 15))
int n,m,res,g[N][N];
tuple<int,int,int> e[M];
void clear()
{
res = 0x7f7f7f7f7f7f7f7f;
rep(i,1,n) rep(j,1,n) {
if(i != j) g[i][j] = INF;
}
}
void solve()
{
cin >> n >> m; clear();
for(int i = 1, u,v,w; i <= m; i++) {
cin >> u >> v >> w; g[u][v] = g[v][u] = 1; e[i] = {u,v,w};
}
rep(k,1,n) rep(i,1,n) rep(j,1,n) {
down(g[i][j], g[i][k] + g[k][j]);
}
for(int i = 1,u,v,w; i <= m; i++)
{
u = get<0>(e[i]), v = get<1>(e[i]), w = get<2>(e[i]);
int now = min(g[1][u] + g[v][n], g[1][v] + g[u][n]);
for(int j = 1; j <= n; j++) {
down(now, min(g[u][j], g[v][j]) + 1 + g[1][j] + g[j][n]);
}
down(res, w * (now + 1));
}
cout << res << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int Q; cin >> Q; while(Q--) solve();
return 0;
}
参考文献: