称一个矩阵为魔法矩阵,当且仅当满足一下三点:
- ∀ 1 ≤ i , j ≤ n , a i , j = a j , i \forall 1 \le i,j \le n, a_{i,j}=a_{j,i} ∀1≤i,j≤n,ai,j=aj,i
- ∀ 1 ≤ i ≤ n , a i , i = 0 \forall 1 \le i \le n, a_{i,i} = 0 ∀1≤i≤n,ai,i=0
- ∀ 1 ≤ i , j , k ≤ n , a i , j ≤ max ( a i , k , a k , j ) \forall 1 \le i,j,k\le n, a_{i,j} \le \max(a_{i,k}, a_{k,j}) ∀1≤i,j,k≤n,ai,j≤max(ai,k,ak,j)
询问一个矩阵是否为魔法矩阵
( n ≤ 2500 n \le 2500 n≤2500)
将矩阵抽象成一个有
n
n
n 个点
n
2
n^2
n2 条边的无向完全图,边权为
a
i
,
j
a_{i,j}
ai,j。
记
f
i
,
j
f_{i,j}
fi,j 表示从
i
i
i 出发到
j
j
j 的所有路径上最长的边的最小值,显然
a
i
,
j
≥
f
i
,
j
a_{i,j} \ge f_{i,j}
ai,j≥fi,j。
根据题意,因为
a
i
,
j
≤
max
(
a
i
,
k
,
a
k
,
j
)
a_{i,j} \le \max(a_{i,k}, a_{k,j})
ai,j≤max(ai,k,ak,j),而
a
i
,
k
≤
max
(
a
i
,
l
,
a
l
,
k
)
a_{i,k} \le \max(a_{i,l},a_{l,k})
ai,k≤max(ai,l,al,k),所以
a
i
,
j
≤
max
(
a
i
,
k
1
,
a
k
1
,
k
2
.
.
.
a
k
m
,
j
)
a_{i,j} \le \max(a_{i,k_1}, a_{k_1,k_2}...a_{k_m, j})
ai,j≤max(ai,k1,ak1,k2...akm,j),即
a
i
,
j
≤
f
i
,
j
a_{i,j} \le f_{i,j}
ai,j≤fi,j。
所以
a
i
,
j
=
f
i
,
j
a_{i,j} = f_{i,j}
ai,j=fi,j,这样就只要用
M
S
T
\mathrm{MST}
MST 与
D
F
S
\mathrm{DFS}
DFS 就可以在
O
(
n
2
)
O(n^2)
O(n2) 判断条件了。
#include <cstdio>
#include <cstring>
#define Min(_A, _B) (_A < _B ? _A : _B)
#define Max(_A, _B) (_A > _B ? _A : _B)
#define R register
int n, a[2510][2510];
bool vis[2510]; int dis[2510], from[2510], Point[2510], Next[5010], To[5010], W[5010], q;
void Add(R int u, R int v, R int w)
{
Next[++q] = Point[u]; Point[u] = q; To[q] = v; W[q] = w;
Next[++q] = Point[v]; Point[v] = q; To[q] = u; W[q] = w;
}
bool DFS(R int u, R int from, R int pos, R int val)
{
if(a[pos][u] != val) return 1;
for(R int j = Point[u]; j; j = Next[j]) if(To[j] != from && DFS(To[j], u, pos, Max(W[j], val))) return 1;
return 0;
}
int main()
{
scanf("%d", &n);
for(R int i = 1; i <= n; ++i)
for(R int j = 1; j <= n; ++j)
scanf("%d", &a[i][j]);
for(R int i = 1; i <= n; ++i)
for(R int j = 1; j <= n; ++j)
if(a[i][j] != a[j][i])
{
puts("NOT MAGIC");
return 0;
}
memset(dis, 127, sizeof(dis)); dis[1] = 0;
for(R int i = 1; i <= n; ++i)
{
R int pos = 0;
for(R int j = 1; j <= n; ++j) if(!vis[j] && dis[pos] > dis[j]) pos = j;
if(i > 1) Add(pos, from[pos], dis[pos]);
vis[pos] = 1;
for(R int j = 1; j <= n; ++j) if(!vis[j] && a[pos][j] < dis[j])
{ from[j] = pos; dis[j] = a[pos][j]; }
}
for(R int i = 1; i <= n; ++i) if(DFS(i, i, i, 0)){ puts("NOT MAGIC"); return 0; }
puts("MAGIC");
return 0;
}