怎么要微信才能注册账号/fn
要计算删掉某个点,最短路之和。容易想到,从 Floyd 的角度考虑,就是不使用那个点为中转点。
到这里想歪了,想从最短路图来考虑。
正解是,设 \(solve(l, r)\) 表示不使用 \([l, r]\) 的点为中转点。这里考虑分治,容易从 \(solve(l, r)\) 转移到 \(solve(l, mid)\) 和 \(solve(mid+1, r)\)。
反思一下优化在什么地方。
如果枚举删除点在什么地方,再枚举中转点,显然太浪费了。
这里通过分治减少了中转点的重复枚举。
不用某个点,相当于使用一段前缀和一段后缀作为中转点。但是合并两段区间的 \(dis\) 值是困难的。用分治的方法可以做到连续段尽量少重复计算。
时间复杂度 \(T(n)=O(n^3)+2T(n/2)\),由主定理得 \(T(n)=O(n^3)\)。
#define int long longint ans;
void solve(int l, int r, vector<vector<int> > &dis) {if(l == r) {upw(i, 1, n) upw(j, 1, n) if(i != l && j != l)ans += dis[i][j] < 1e18 ? dis[i][j] : -1ll;return;}vector<vector<int> > dis1 = dis, dis2 = dis;int mid = l + r >> 1;upw(k, l, mid) upw(i, 1, n) upw(j, 1, n) vmin(dis2[i][j], dis2[i][k] + dis2[k][j]);upw(k, mid+1, r) upw(i, 1, n) upw(j, 1, n) vmin(dis1[i][j], dis1[i][k] + dis1[k][j]);solve(l, mid, dis1), solve(mid+1, r, dis2);
}