E. Journey
Problem - E - Codeforces
\(kruskal\)重构树, 欧拉路径
首先不考虑操作二,那么题目就是问走过所有边回到 \(1\) 的最短路径,如果均仅走过一次,那么整个路径构成欧拉回路,答案为 \(\sum_i w_i\) ,否则,将有一些边走过多次。
那么操作二可以看作是在原图上建立一些虚拟边,避免走过重复边,缩小代价,答案就是虚拟边和原有的代价之和。
根据欧拉回路的定义,这些虚拟边根据每个点的度来建立,每两个度数为奇数的点(下文称为奇点)间可以建立
虚拟边的代价是两点之间路径编号最大的边的边权,且不要求简单路径。很容易想到并查集维护连通性即可确定虚拟边的代价,但复杂度较高。
考虑\(kruskal\)重构树,以原图上每个点作为叶子节点,按序号从小到大枚举边,将边作为非叶子节点,链接原图上非叶子的两个节点的祖先。
则两个奇点 \(u\) , \(v\) 间的虚拟边的代价即为重构树上两个点 \(u\) , \(v\) 的公共祖先的最小权值,可以\(O(n+m)\)计算完
class DSU{
public:vector<int> p;DSU(){}DSU(int n): p(n){iota(p.begin(), p.end(), 0);};int find(int x) {return x == p[x] ? x : p[x] = find(p[x]);}
};void bluket() {int n = R, m = R;ll ans = 0;vector<ll> a(m + 1), deg(n+1);vector<vector<int>> adj(n + m + 1);DSU ds(n + m + 1);for (int i = 1; i <= m; i++) {int u = R, v = R, w = R;int fu = ds.find(u), fv = ds.find(v);deg[u]++, deg[v]++;ans += w;a[i] = w;// kurskal 重构树, 按节点编号合并if(fu == fv) {adj[i + n].push_back(fu);ds.p[fu] = i + n;}else {adj[i + n].push_back(fu);ds.p[fu] = i + n;adj[i + n].push_back(fv);ds.p[fv] = i + n;}}// u 当前节点,fw 祖宗节点到当前节点可以采用的最小边权auto dfs = [&](auto&& dfs, int u, ll fw) -> int {// 到达表示重构树上的叶子节点,即原图的点, 返回是不是奇点if(u <= n) return deg[u] % 2;fw = min(fw, a[u - n]);int cnt = 0;for (int v : adj[u]) {cnt += dfs(dfs, v, fw);}// 所有未处理的子树上的奇点的最近公共祖先是当前的 uans += cnt / 2 * fw;cnt %= 2;// 传递未处理的奇点return cnt;};dfs(dfs, n + m, a[m]);cout << ans << endl;
}