Codeforces 632F Magic Matrix

tag: 最小生成树

传送门:Codeforces - 632F

对于一个 \(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;
}
posted @ 2025-07-15 20:04  f2021ljh  阅读(15)  评论(0)    收藏  举报