题意:给定n条边,连接编号从1到1e9之间的点,一次操作可以改变一条边连接的一个端点到另一个端点,也就是将第i条边(u,v)变成(u,w),v≠w,问最少次数操作使得这张图的所有边联通,输出操作次数k,以及每次操作的i,v,w。
分析:先将点离散化。假设有n个联通块s1,s2....sn,可以将连通块si内一条边端点改成si+1内一个点,但是改变这条边不能使得原有的连通块内的边变得不连通。dfs这个连通块最后一个被访问的边就是满足要求的边。
代码:
#include <bits/stdc++.h> #define mp make_pair #define debug(x) cout << #x << ": " << x << endl #define pb push_back typedef long long LL; const int maxn = 4e5 + 10; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; using namespace std; typedef pair<int, int> p; p e[maxn], tail[maxn]; int val[maxn], len = 0, n, t, ord[maxn], vis[maxn], tot = 0, head[maxn], to[maxn], tots; vector<int> g[maxn]; void dfs(int x, int pre) { vis[x] = true; for (int i = 0; i < g[x].size(); ++i) { int v = to[g[x][i]]; if (!vis[v]) { tail[tot] = mp(g[x][i] / 2 + 1, v); dfs(v, x); } } } int main() { ios::sync_with_stdio(0); cin.tie(0); cin >> t; while (t--) { cin >> n; len = 0; tot = 0; tots = 0; for (int i = 0; i < n; ++i) { cin >> e[i].first >> e[i].second; val[len++] = e[i].first; val[len++] = e[i].second; } sort(val, val + len); len = unique(val, val + len) - val; for (int i = 0; i < len; ++i) { vis[i] = 0; g[i].clear(); } for (int i = 0; i < n; ++i) { int fu = lower_bound(val, val + len, e[i].first) - val; ord[fu] = e[i].first; e[i].first = fu; fu = lower_bound(val, val + len, e[i].second) - val; ord[fu] = e[i].second; e[i].second = fu; to[tots] = e[i].second; g[e[i].first].pb(tots++); to[tots] = e[i].first; g[e[i].second].pb(tots++); } /*for (int i = 0; i < tots; ++i) cout << to[i] << endl;*/ for (int i = 0; i < len; ++i) { if (!vis[i]) { ++tot; head[tot] = i; dfs(i, -1); } } cout << tot - 1 << "\n"; for (int i = 1; i < tot; ++i) { cout << tail[i].first << " " << ord[tail[i].second] << " " << ord[head[i + 1]] << "\n"; } } }