AT2166 Rotate 3x3
传送门
这个题网上有两种做法,一种是树状数组的,还有一种是暴力模拟的,暴力模拟显然不够优美,所以我用的树状数组
显然可以从初状态推到目标状态,我们也可以考虑倒推回去
首先可以容易发现每列的数字是不变的,所以可以把一些奇奇怪怪的情况先处理掉
每次旋转会使矩阵翻转并且每列取反,发现行其实没什么用,可以丢掉
然后我们定义一个序列的奇偶性为:反的列的数量的奇偶性
那么我们现在就转化为一个问题:
给你一个长度为\(n\)的序列,每次可以选择连续的\(3\)个数翻转并取反,问能否达到\(1,2,3...n\)的状态
假如不考虑符号,考虑翻转操作其实就是交换同一奇偶性的两个相邻数
然后发现奇数列和偶数列是相对独立的,所以可以把奇数列和偶数列拎出来单独处理
然后就是一个经典问题了:每次交换两个相邻的数,使序列有序最少的交换次数是逆序对个数
所以用树状数组求出逆序对就好了。
然后考虑一下符号,奇数列每次翻转会导致偶序列的一个位置被取反(也就是改变偶数列的奇偶性)
所以求出逆序对数后,就可以算出有序状态下的奇数列和偶数列的奇偶性
然后我们手玩一下可以得知,我们可以任意取反同一奇偶性的相邻两个数而不对其他数造成影响
手玩过程如下(定义大写字母为反的,小写字母为正的):
\[\begin{align}
&abcde\\
&CBAde\\
&CBEDa\\
&ebcDa\\
&ebAdC\\
&aBEdC\\
&aBcDe----偶数列取反\\
&adCbe\\
&cDAbe\\
&cBade\\
&AbCde----奇数列取反\\
\end{align}
\]
这样我们就只需要奇数列和偶数列的奇偶性都为偶就是可以达到的状态
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
void read(int &x){
char ch;bool ok;
for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
#define rg register
const int maxn=1e5+10;bool flag;
int n,m,x[maxn*4],y[maxn*4],z[maxn*4],mp[4][maxn];
int f[maxn*4],w[maxn*4],tot,ans,ans1,s[2],ss[2];
#define lowbit(i) (i&(-i))
void add(int x){for(rg int i=x;i<=m;i+=lowbit(i))f[i]++;}
int get(int x){int ans=0;for(rg int i=x;i;i-=lowbit(i))ans+=f[i];return ans;}
int main(){
read(m),n=3;
for(rg int i=1;i<=n;i++)
for(rg int j=1;j<=m;j++)
x[i+3*j-3]=i&1,y[i+3*j-3]=j&1,z[i+3*j-3]=j;
for(rg int i=1;i<=n;i++)for(rg int j=1;j<=m;j++)read(mp[i][j]);
for(rg int i=1;i<=n;i++)
for(rg int j=1;j<=m;j++){
int u=1+z[mp[i][j]]*3-3,v=2+z[mp[i][j]]*3-3,w=3+z[mp[i][j]]*3-3;
bool ok1=0,ok2=0,ok3=0;
if(mp[1][j]==u||mp[2][j]==u||mp[3][j]==u)ok1=1;
if(mp[1][j]==v||mp[2][j]==v||mp[3][j]==v)ok2=1;
if(mp[1][j]==w||mp[2][j]==w||mp[3][j]==w)ok3=1;
if(!(ok1&ok2&ok3))flag=1;
if(((i&1)^x[mp[i][j]])||((j&1)^y[mp[i][j]]))flag=1;
}
if(flag){printf("No\n");return 0;}
for(rg int i=1;i<=m;i+=2)if(mp[1][i]!=1+3*z[mp[1][i]]-3)s[1]^=1;
for(rg int i=2;i<=m;i+=2)if(mp[1][i]!=1+3*z[mp[1][i]]-3)s[0]^=1;
for(rg int i=1;i<=m;i+=2)w[++tot]=z[mp[1][i]];
for(rg int i=1;i<=tot;i++)ans+=i-get(w[i])-1,add(w[i]);
for(rg int i=1;i<=m;i++)f[i]=0;tot=0;
for(rg int i=2;i<=m;i+=2)w[++tot]=z[mp[1][i]];
for(rg int i=1;i<=tot;i++)ans1+=i-get(w[i])-1,add(w[i]);
s[1]^=(ans1&1),s[0]^=(ans&1);
if(s[0]||s[1])printf("No\n");
else puts(s[0]!=s[1]?"No":"Yes");
}