Codeforces 632F Magic Matrix
tag: 最小生成树
对于一个 \(n\times n\) 的非负整数矩阵 \(A\),如果满足:
- \(A\) 是对称阵;
- \(A_{ii}=0\);
- 对于所有三元组 \((i,j,k)\)(\(i,j,k\) 不一定不同),满足 \(A_{ij}\le\max\{A_{ik},A_{jk}\}\),
则称 \(A\) 是一个魔法矩阵。
给定 \(n\) 和 \(A\),请判断 \(A\) 是否是一个魔法矩阵。
\(1\le n\le2500\),\(0\le A_{ij}\le10^9\)。
显然,\(A\) 对应着一个非负权无向图的邻接矩阵。
下面称满足 \(\forall x\),\(A_{ij}\le\max\{A_{ix},A_{xj}\}\) 的一组 \((i,j)\) 为合法的。
将条件扩展到三个点:假设 \((i,j),(j,k),(i,k)\) 都合法,则对于任意 \(x\),\(i,j,k\) 满足
\[A_{ik}\le\max\{A_{ij},A_{jk}\}\le\max\{A_{ix},A_{xj},A_{jk}\}.
\]
像这样,我们总可以选任意一个 \(x\) 继续进行扩展,最终就变成:\((i,j)\) 是合法的当且仅当对于任意一个包含 \((i,j)\) 的环 \(ip_1p_2\cdots p_kj\),一定有
\[A_{ij}\le\max\{A_{ip_1},A_{p_1p_2},\cdots,A_{p_{k-1}p_k},A_{p_kj}\},
\]
即对于包含 \((i,j)\) 的任一环,\((i,j)\) 不是环上的唯一最大值。
这与 最小生成树 的性质是相似的。考虑 Kruskal 算法的过程,对于最小生成树上的边 \((i,j)\),它一定满足这个性质,否则,\(i\) 和 \(j\) 一定不会被 \((i,j)\) 加入到最小生成树中,而是之前就已经通过更小的边加入了最小生成树。
对于非树边 \((i,j)\),在最小生成树上检查 \(i\) 和 \(j\) 之间的路径上的 max 是否大于等于 \(A_{ij}\) 即可。
实现:
- 由于是稠密图,用 Prim 算法更快。
- 注意 Prim 算法中保存 MST 中边的方式,需要加一个
from[x]数组,来存是由哪条边使x入树的。
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
int const N = 2.5e3 + 10;
int n, a[N][N];
int d[N], from[N];
bool in[N];
vector<int> tr[N];
void Prim() {
memset(d, 0x3f, sizeof d);
memset(in, 0, sizeof in);
d[1] = 0;
in[1] = 1;
for (int i = 2; i <= n; i++) d[i] = a[1][i], from[i] = 1;
for (int i = 1; i < n; i++) {
int x = 0;
for (int j = 1; j <= n; j++) {
if (!in[j] && (x == 0 || d[j] < d[x])) {
x = j;
}
}
in[x] = 1;
tr[from[x]].pb(x);
tr[x].pb(from[x]);
for (int y = 1; y <= n; y++) {
if (!in[y] && a[x][y] < d[y]) {
d[y] = a[x][y];
from[y] = x;
}
}
}
return;
}
int mx[N];
void dfs(int rt, int u, int fa = 0) {
if (u != rt && mx[u] < a[rt][u]) {
cout << "NOT MAGIC\n";
exit(0);
}
for (int v : tr[u]) {
if (v == fa) continue;
mx[v] = max(mx[u], a[u][v]);
dfs(rt, v, u);
}
return;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
if (i > j && a[i][j] != a[j][i]) {
cout << "NOT MAGIC\n";
return;
}
if (i == j && a[i][j] != 0) {
cout << "NOT MAGIC\n";
return;
}
}
}
Prim();
for (int i = 1; i <= n; i++) {
memset(mx, 0, sizeof mx);
dfs(i, i);
}
cout << "MAGIC\n";
return;
}
signed main() {
cin.tie(0)->sync_with_stdio(false);
int tt = 1;
// cin >> tt;
while (tt--) solve();
return 0;
}

浙公网安备 33010602011771号