AGC006E Rotate 3x3(思维题)

题目:洛谷AT2166AGC006E

题目描述:

给定一个\(3 \times n\)的矩阵,每个格子上面都填写了一个互不相同的\(1\)~\(3n\)的正整数,你每次可以选择一个\(3 \times 3\)的矩阵,并将它翻转\(180^{\circ}\)

问能否将这个矩阵变为一个满足格子\((i,j)\)上的数字为\(i+3j-3\)

\(5 \leq n \leq 10^5\)

蒟蒻题解:

首先可以把一列怎么翻转都不可能满足条件的情况先判掉

由于每次选择\(3 \times 3\)的矩阵翻转,对于一列它原本位置的奇偶性是不会变的,可以分奇偶考虑,把不满足当前位置和结尾位置奇偶性相同的情况先判掉

我们可以把每一列缩成一个数字,从上到下为正,从下到上为负

对于一次翻转操作,假设它以某个奇数列为中心,那么这个奇数列的数字会变为原来的相反数,而它旁边的两个偶数列的数则会交换并变为原来的相反数

把条件拆开来考虑,一次操作相当于会使奇数列负数个数的奇偶性变化,也会使偶数列逆序对数的奇偶性变化

以偶数列为中心同理

那么如果这个矩阵合法,它一定会满足奇数列的负数个数的奇偶性与偶数列逆序对数的奇偶性一样,并且偶数列的负数个数的奇偶性与奇数列逆序对数的奇偶性一样

那是不是满足这个条件的一定可以呢?打表告诉我们这是对的

再回到题目中,把只隔一列的操作拓展到隔着奇数列操作

还是以奇数列为中心,要交换两个偶数列,手玩一下,发现他们在操作的过程中,他们中间的奇数列只有一列的奇偶性会改变,而那个奇数列正是包含这两个列的那次翻转操作的中心,即这两个列位置变化过程中相交的地方

那如果交换连续交换两次相同的偶数列,那么我们可以改变中间奇数列中其中两个列的正负性

以偶数列为中心同理

考虑我们先不管正负性,先把每个数都跳到对应的位置上,因为奇偶之间是没有影响的,再加上不满足初始位置和结尾位置奇偶性不同的前面已经判掉了,所以这一定是可以做到的

又由于每次翻转都会改变奇数列的负数个数的奇偶性与偶数列逆序对数的奇偶性,或者是改变偶数列的负数个数的奇偶性与奇数列逆序对数的奇偶性,所以当前状态和初始状态是等价的

如果满足奇数列的负数个数的奇偶性与偶数列逆序对数的奇偶性一样,并且偶数列的负数个数的奇偶性与奇数列逆序对数的奇偶性一样,那么对于两个奇数列中的负数,我们可以通过两个包含他们的偶数列去消掉这两个负数,对于两个偶数列中的负数同理

那么如果边界上没有负数,我们一定可以把负数全消掉

那如果负数在边界上呢?

观察到\(n \geq 5\),而不是\(n \geq 3\)

假设\(1\)为负数,我们可以通过如果操作将\(1\)的负号转到后面去:
-1 A B C D
-B -A 1 C D
-B -C -1 A D
1 C B A D
1 -A -B -C D

这样我们就可以把边界上的负数消掉了

所以只要满足奇数列的负数个数的奇偶性与偶数列逆序对数的奇偶性一样,并且偶数列的负数个数的奇偶性与奇数列逆序对数的奇偶性一样就可以了

这样我们只需要计算奇数位的逆序对个数的奇偶性和偶数位的逆序对个数的奇偶性即可

直接树状数组优化计算逆序对是\(\mathcal O(n \log n)\)

由于我们只要计算逆序对的奇偶性,在一串互不相同的数字中,交换任意两个数字均会改变逆序对的奇偶性,所以我们只需要在第\(i\)位时,把\(i\)所在的位置和第\(i\)为交换,统计交换次数的奇偶性即可

时间复杂度\(\mathcal O(n)\)

参考程序:

#include<bits/stdc++.h>
using namespace std;
#define Re register int

const int N = 100005;
int n, a[N][3], c[N], d[N];
bool b[2];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1;
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar(); 
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans; 
}

inline void gg()
{
	puts("No");
	exit(0);
}

int main()
{
	scanf("%d", &n);
	for (Re i = 0; i < 3; ++i)
		for (Re j = 1; j <= n; ++j) a[j][i] = read();
	for (Re i = 1; i <= n; ++i)
	{
		if (!(!(a[i][2] % 3) && a[i][1] == a[i][0] + 1 && a[i][2] == a[i][1] + 1) && !(!(a[i][0] % 3) && a[i][1] == a[i][0] - 1 && a[i][2] == a[i][1] - 1)) gg();
		c[i] = a[i][1] / 3 + 1;
		if ((c[i] ^ i) & 1) gg();
		d[c[i]] = i, b[i & 1] ^= !(a[i][0] % 3);
	}
	for (Re i = 1; i <= n; ++i)
	{
		if (c[i] == i) continue;
		d[c[i]] = d[i], c[d[i]] = c[i], b[(i & 1) ^ 1] ^= 1;
	}
	puts(b[0] | b[1] ? "No" : "Yes");
	return 0;
}
posted @ 2021-06-09 13:30  clfzs  阅读(64)  评论(0编辑  收藏  举报