P6628 [省选联考 2020 B 卷] 丁香之路 题解
首先考虑题目中路径权值的含义:\(i,j\) 两点之间的最短路就是 \(|i-j|\) 直接连边。
题目要求从 \(s\) 遍历到每个点,到终点每个 \(x\) 的最短时间。于是我们不妨枚举每个 \(x\),考虑在 \(O(n)\) 至 \(O(n\log n)\) 的时间复杂度里解决问题。
观察到题目的定义和欧拉回路的定义很相似。若加上 \((s,x)\) 的虚边,就是一个欧拉回路了。考虑欧拉回路的判定条件是所有点的度数都是偶数。于是我们的任务转变为了将存在的这些个奇度点通过连一些边的方式变成偶度点,同时要保证图联通。
偶度点的限制更不好处理一些,于是让我们先考虑偶度点的限制。在解决问题之前需要注意到本题中特殊权值带来的性质:对于 \(a,b\) 两点,最短路是 \(|a-b|\),于是不妨在 \(a,b\) 之间的每两个相邻的点之间都连一条边,这样不仅只改变了 \(a,b\) 点度数的奇偶性,还尽可能多地连通了连通块。
于是我们将所有奇度点排序为 \(a_1,a_2,a_3\cdots,a_n\),将 \(a_1,a_2;a_3,a_4\) 这样两两配对一定最优。对于连通性问题,首先容易发现的是上述的连边一定不劣,证明的话考虑无论如何奇度点会找到另一个奇度点配对,那对 \(a_1\),如果不找 \(a_2\),倘若找到 \(a_t\),那么这样更新的实质和 \(a_1\rightarrow a_2\rightarrow a_t\) 是一致的,依此类推地去考虑分析。那么现在的图都是偶度点,加一条边就必须加双向的。连边的原则仍然是尽量连相邻的边,于是将连续的两两不相邻的边加入最小生成树去跑即可。
时间复杂度的话是 \(O(n^2\log n)\) 的,瓶颈是最小生成树。
代码:
#include <bits/stdc++.h>
#define N 2505
#define int long long
using namespace std;
int n, m, s;
int fa[N], fz[N];
int fnd(int x) {return x == fa[x] ? x : fa[x] = fnd(fa[x]);
}
void mge(int x, int y) {x = fnd(x), y = fnd(y);fa[x] = y;
}
int deg[N];
struct Node {int x, y, dis;bool operator < (const Node &a) const {return dis < a.dis;}
};
int ans, sum;signed main() {ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m >> s;for (int i = 1; i <= n; i++)fa[i] = i;for (int i = 1; i <= m; i++) {int x, y;cin >> x >> y;deg[x]++, deg[y]++;sum += abs(x - y);mge(x, y);}for (int i = 1; i <= n; i++)fz[i] = fa[i];for (int i = 1; i <= n; i++) {deg[s]++;deg[i]++;ans = sum;for (int j = 1; j <= n; j++)fa[j] = fz[j];int t = 0;for (int j = 1; j <= n; j++)if (deg[j] & 1) {if (t) {for (int k = t; k < j; k++)mge(k, k + 1);ans += j - t;t = 0;}else t = j;}vector<int>p;for (int j = 1; j <= n; j++)if (deg[j]) p.push_back(j);int l = p.size();vector<Node>v;for (int j = 1; j < l; j++)if (fnd(p[j - 1]) != fnd(p[j]))v.push_back({p[j - 1], p[j], p[j] - p[j - 1]});sort(v.begin(), v.end());for (auto tmp : v)if (fnd(tmp.x) != fnd(tmp.y))ans += tmp.dis << 1ll, mge(tmp.x, tmp.y);cout << ans << " ";deg[s]--;deg[i]--;}return 0;
}