题目大意:判断 $n \times m$ 的八数码问题是否有解。$n, m < 1000$
横向交换时逆序对数不变,纵向交换相当于将区间 $[l,r]$ 循环右移一位,因此当 $m$ 为奇数时,纵向交换一次逆序对数奇偶性不变,$m$ 为偶数时,纵向交换一次逆序对数奇偶性取反。
#include <cstdio>
#include <cstring>
typedef long long LL;
const int N = 1000000;
int a[N], b[N], cnt;
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x;
}
int lowbit(int x) {
return x & (-x);
}
void update(int x) {
while (x <= cnt) ++b[x], x += lowbit(x);
}
int query(int x) {
int res = 0;
while (x) res += b[x], x -= lowbit(x);
return res;
}
int main() {
while (1) {
int n = read(), m = read(), k;
if (!n) break;
cnt = 0;
memset(b, 0, sizeof b);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) {
int x = read();
if (x) a[++cnt] = x;
else k = i;
}
LL ans = 0;
for (int i = cnt; i >= 1; --i) {
ans += query(a[i] - 1);
update(a[i]);
}
if (m & 1) {
if (ans & 1) puts("NO");
else puts("YES");
} else {
if ((ans + m - k) & 1) puts("NO"); //空格从初始位置移到目标位置后, 逆序对数奇偶性取反m-k次
else puts("YES");
}
}
return 0;
}