版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/86496967
【思路要点】
- 考虑快速模拟 算法。
- 具体来说,对于所有长度为 的边,我们需要计算有多少条连接了两个由长度不足 的边连成的不同联通块,并不会与其余长度为 的边连接的联通块成环,记条数为 ,则 。
- 以所有给定的点为起点,进行 ,求得将每一个点 修改到某一个起点的最小修改距离 。
- 将一条最小生成树上长度为 的边看做 次异或一位操作的结果,可以证明,这些操作存在相邻的中间结果 ,满足 ,即路径最中间相邻的两点 满足 。
- 进而有 ,因此,我们可以直接从小到大枚举 ,计算出 ,然后将尚未连通的部分用并查集合并起来,具体实现可以参见代码。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXV = (1 << 20) + 5; const int MAXM = 25; const int INF = 1e9; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, a[MAXN]; int dist[MAXV], f[MAXV]; int F(int x) { if (f[x] == -1) return -1; if (f[x] == x) return x; else return f[x] = F(f[x]); } int main() { freopen("ji.in", "r", stdin); freopen("ji.out", "w", stdout); int T; read(T); while (T--) { read(m), read(n); for (int i = 1; i <= n; i++) { static char s[MAXM]; scanf("\n%s", s + 1); int res = 0; for (int j = 1; j <= m; j++) res = res * 2 + (s[j] == 'L'); a[i] = res; } int goal = (1 << m) - 1; for (int i = 0; i <= goal; i++) { f[i] = -1; dist[i] = INF; } static int q[MAXV]; int l = 1, r = 0; for (int i = 1; i <= n; i++) { if (f[a[i]] == -1) q[++r] = a[i]; f[a[i]] = a[i], dist[a[i]] = 0; } while (l <= r) { int pos = q[l++]; for (int i = 0; i <= m - 1; i++) { int dest = pos ^ (1 << i); if (dist[pos] + 1 < dist[dest]) { dist[dest] = dist[pos] + 1; q[++r] = dest; } } } int ans = 0; for (int len = 0; len <= m; len++) { for (int pos = 0; pos <= goal; pos++) if (dist[pos] == len) { for (int i = 0; i <= m - 1; i++) { int dest = pos ^ (1 << i); if (dist[dest] < dist[pos]) { if (f[pos] == -1) f[pos] = F(dest); else if (F(pos) != F(dest)) { ans += dist[pos] + dist[dest] + 1; f[F(pos)] = F(dest); } } } } for (int pos = 0; pos <= goal; pos++) if (dist[pos] == len) { for (int i = 0; i <= m - 1; i++) { int dest = pos ^ (1 << i); if (dist[dest] == dist[pos]) { if (f[pos] == -1) f[pos] = F(dest); else if (f[dest] == -1) f[dest] = F(pos); else if (F(pos) != F(dest)) { ans += dist[pos] + dist[dest] + 1; f[F(pos)] = F(dest); } } } } } writeln(ans); } return 0; }