原文链接 https://www.cnblogs.com/cly-none/p/9800105.html
题意:给出一个三行(n)列的矩阵。问它能否由满足(a_{ij} = 3(j-1) + i)的初始矩阵通过有限次中心对称其中的一个(3 imes 3)子矩阵的操作得到。
(5 leq n leq 10^5)
首先,我们容易发现,无论如何操作,每一列都是形如(3k+1, \, 3k+2, \, 3k+3)的3个数,并且要么是顺序要么是倒序。我们记恰是原来第(i)列的一列的权值为(i),恰是原来第(i)列上下翻转为(-i)。那么,我们每一次操作,就变成了选取相邻的3列,交换左边和右边两列的权值并把这3个权值取负。
然后,我们还能得到偶数权值只可能存在于偶数列中,奇数权值只存在于奇数列中。
然而,仅此还不足以成为充分条件,我们需要进一步分析。
一般而言,这类问题的关键在于发现“不变量”,即一次操作前后都不会发生变化的量。
于是考虑一次操作。它由两个部分组成:把所有列按奇偶性分为两组后,交换并取反一组中相邻的两列;并取反另一组中对应位置的一列。让我们先勉为其难地放弃充分性,分开考虑这两部分而忽略一部分联系:
- 操作1:交换两个相邻元素。
- 操作2:取反一个元素。
这两个操作都很简洁,容易分析。
对于操作1,通过逆序对分析我们能得到,如果初始状态和结束状态已知,那么它操作总数的奇偶性是固定的。同样,操作2也有这个性质。
同时,我们注意到,奇数列中操作1的个数等于偶数列中操作2的个数。这意味着,奇数列的逆序对数的奇偶性与偶数列中负数个数的奇偶性相同。反之亦然。
又是一个充分条件,但已经挺复杂了。利用搜索程序验证,在(n)很小时,这个条件是必要的。
因此我们尝试证明这个条件是必要的。事实上,既然都写出搜索,我们就能发现在(n=5)时,这两种情况是可能的:
这意味着,只要先把所有元素移动到对应位置,就可以不断任取同奇偶的两列调整正负。因为奇偶性是保证的,所以一定能得到最终状态。所以本题标算搜索
时间复杂度(O(n log n))。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int mat[4][N],val[N],n,num[2],v[N];
#define lowbit(x) ((x) & (-(x)))
void add(int p,int val) {
for ( ; p <= n ; p += lowbit(p))
v[p] += val;
}
int query(int p) {
int ret = 0;
for ( ; p ; p -= lowbit(p))
ret += v[p];
return ret;
}
void fail() {
puts("No");
exit(0);
}
int main() {
scanf("%d",&n);
for (int i = 1 ; i <= 3 ; ++ i)
for (int j = 1 ; j <= n ; ++ j)
scanf("%d",&mat[i][j]);
for (int i = 1 ; i <= n ; ++ i) {
if (mat[1][i] % 3 == 0) {
if (mat[2][i] == mat[1][i] - 1 && mat[3][i] == mat[1][i] - 2)
val[i] = - mat[1][i] / 3;
else fail();
} else if (mat[3][i] % 3 == 0) {
if (mat[2][i] == mat[3][i] - 1 && mat[1][i] == mat[3][i] - 2)
val[i] = mat[3][i] / 3;
else fail();
} else fail();
}
for (int i = 1 ; i <= n ; i += 2) {
if (val[i] < 0) num[1] ^= 1, val[i] = -val[i];
if (val[i] % 2 == 0) fail();
}
for (int i = 2 ; i <= n ; i += 2) {
if (val[i] < 0) num[0] ^= 1, val[i] = -val[i];
if (val[i]&1) fail();
}
for (int i = 1 ; i <= n ; i += 2) {
if ((query(n) - query(val[i]))&1) num[0] ^= 1;
add(val[i],1);
}
memset(v,0,sizeof v);
for (int i = 2 ; i <= n ; i += 2) {
if ((query(n) - query(val[i]))&1) num[1] ^= 1;
add(val[i],1);
}
if ((!num[0]) && (!num[1]))
puts("Yes");
else puts("No");
return 0;
}
小结:本题的解法主要有两点存在启发性:一是推导充分条件时,可以暂时放弃充分性来得到新的推论(这和求和时引入新的(sum)有异曲同工之妙);二是一个搜索程序无伤大雅,除找规律乱搞外,还能成为解题的助力。