题目大意:给一张$n(n\leqslant2\times10^5)$个点$m(m\leqslant2\times10^5)$条边的无向带权图,定义一次操作为把一条边边权加一,求要使最小生成树唯一的最少操作数。
题解:先求一个最小生成树,对于任意一条不在最小生成树上的边,只有当它连上后形成的环上有和它边权相同的边时,最小生成树才是不唯一的,这是就要把这条边边权加一,并且,相同的那一条边一定是这个环上边权最大的边。所以可以用树上倍增求这条边,然后比较一下就行了。
卡点:树上倍增多处写错
C++ Code:
#include <algorithm>
#include <cstdio>
#define maxn 200010int 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;
}