[CF1108F]MST Unification
题目大意:给一张$n(n\leqslant2\times10^5)$个点$m(m\leqslant2\times10^5)$条边的无向带权图,定义一次操作为把一条边边权加一,求要使最小生成树唯一的最少操作数。
题解:先求一个最小生成树,对于任意一条不在最小生成树上的边,只有当它连上后形成的环上有和它边权相同的边时,最小生成树才是不唯一的,这是就要把这条边边权加一,并且,相同的那一条边一定是这个环上边权最大的边。所以可以用树上倍增求这条边,然后比较一下就行了。
卡点:树上倍增多处写错
C++ Code:
#include <algorithm> #include <cstdio> #define maxn 200010 int head[maxn], cnt; struct Edge { int to, nxt, w; inline bool operator < (const Edge &rhs) const { return w < rhs.w; } } e[maxn << 1], E[maxn]; inline void addedge(int a, int b, int c) { e[++cnt] = (Edge) { b, head[a], c }; head[a] = cnt; e[++cnt] = (Edge) { a, head[b], c }; head[b] = cnt; } int n, m; int f[maxn]; int find(int x) { return x == f[x] ? x : (f[x] = find(f[x])); } bool used[maxn]; #define M 17 int fa[maxn][M + 1], Mx[maxn][M + 1], dep[maxn]; void dfs(int u) { for (int i = 1; i <= M; ++i) { fa[u][i] = fa[fa[u][i - 1]][i - 1]; Mx[u][i] = std::max(Mx[u][i - 1], Mx[fa[u][i - 1]][i - 1]); } for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v != *fa[u]) { *fa[v] = u; *Mx[v] = e[i].w; dep[v] = dep[u] + 1; dfs(v); } } } #define chkmin(x, i) res = std::max(res, Mx[x][i]), x = fa[x][i] inline int calc(int x, int y) { int res = 0; if (dep[x] < dep[y]) std::swap(x, y); for (int i = dep[x] - dep[y]; i; i &= i - 1) chkmin(x, __builtin_ctz(i)); if (x == y) return res; for (int i = M; ~i; --i) if (fa[x][i] != fa[y][i]) chkmin(x, i), chkmin(y, i); return std::max(res, std::max(*Mx[x], *Mx[y])); } int main() { scanf("%d%d", &n, &m); for (int i = 0; i < m; ++i) scanf("%d%d%d", &E[i].to, &E[i].nxt, &E[i].w); std::sort(E, E + m); for (int i = 1; i <= n; ++i) f[i] = i; { int num = n - 1; for (int i = 0; i < m && num; ++i) { int u = E[i].to, v = E[i].nxt, x = find(u), y = find(v); if (x != y) { f[x] = y; --num; used[i] = true; addedge(u, v, E[i].w); } } } dfs(1); int ans = 0; for (int i = 0; i < m; ++i) if (!used[i]) ans += E[i].w == calc(E[i].to, E[i].nxt); printf("%d\n", ans); return 0; }