CF1458C Latin Square 题解
一个很傻的做法。
考虑同时维护原矩阵 \(M\),行变换后的矩阵 \(H\),列变换后的矩阵 \(C\)。那么以下操作对他们的影响就是:
- \(R\):\(M,C\) 向右移动,\(H\) 所有元素加一(如果原来是 \(n\) 就变回 \(1\))。
- \(L\):\(M,C\) 向左移动,\(H\) 所有元素减一(如果原来是 \(1\) 就变成 \(n\))。
- \(D\):\(M,H\) 向下移动,\(C\) 所有元素加一(如果原来是 \(n\) 就变回 \(1\))。
- \(U\):\(M,H\) 向上移动,\(C\) 所有元素减一(如果原来是 \(1\) 就变成 \(n\))。
- \(I\):交换 \(M,H\),\(C\) 沿主对角线翻转。
- \(C\):交换 \(M,C\),\(H\) 沿主对角线翻转。
同时维护三个矩阵的交换情况(一个排列),反转情况,加法 \(tag\),平移情况即可。
证明一些结论:排列进行一次逆变换相当于 \((i,p_i)\) 变成 \((p_i,i)\),向右平移对应 \((i,p_i)\) 变成 \((i + 1,p_i)\),行变换之后自然变成 \((p_i,i+1)\),所以是整体加一。又考虑到,行变换后的第 \(i\) 列对应所有 \(i\) 在每一行中的位置,列同理。由于拉丁方的性质行变换的第 \(i\) 列和列变换后的第 \(i\) 行互为逆排列。这样翻转也得到解释了。
这是模拟赛搬的题的弱化,代码中省去了 IO 部分。
#include<bits/stdc++.h>
const int MX=1005,MXK=100000;
int n,k,T;
char op[MXK+5];
short m[3][MX][MX];
inline void Add(short &a,short b){(a+=b)>=n?a-=n:1;}
inline short Sum(short a,short b){return (a+b)>=n?a+b-n:a+b;}
struct node{
short rev[3],tag[3],x[3],y[3],p[3];
node(){rev[0]=rev[1]=rev[2]=tag[1]=tag[2]=tag[0]=x[0]=x[1]=x[2]=y[0]=y[1]=y[2]=0;p[0]=0,p[1]=1,p[2]=2;}
node(int n1,int n2,int n3,int n4,int n5,int n6,int n7,int n8,int n9,int n10,int n11,int n12,int n13,int n14,int n15){
rev[0]=n1;rev[1]=n2;rev[2]=n3;
tag[0]=n4;tag[1]=n5;tag[2]=n6;
x[0]=n7;x[1]=n8;x[2]=n9;
y[0]=n10;y[1]=n11;y[2]=n12;
p[0]=n13;p[1]=n14;p[2]=n15;
}
inline node friend operator*(const node &a,const node &b){
node c;
c.tag[0]=Sum(a.tag[0],b.tag[a.p[0]]);
c.tag[1]=Sum(a.tag[1],b.tag[a.p[1]]);
c.tag[2]=Sum(a.tag[2],b.tag[a.p[2]]);
c.rev[0]=a.rev[0]^b.rev[a.p[0]];
c.rev[1]=a.rev[1]^b.rev[a.p[1]];
c.rev[2]=a.rev[2]^b.rev[a.p[2]];
c.x[0]=a.x[0];c.x[1]=a.x[1];c.x[2]=a.x[2];
c.y[0]=a.y[0];c.y[1]=a.y[1];c.y[2]=a.y[2];
if(!a.rev[0])Add(c.x[0],b.x[a.p[0]]),Add(c.y[0],b.y[a.p[0]]);
else Add(c.x[0],b.y[a.p[0]]),Add(c.y[0],b.x[a.p[0]]);
if(!a.rev[1])Add(c.x[1],b.x[a.p[1]]),Add(c.y[1],b.y[a.p[1]]);
else Add(c.x[1],b.y[a.p[1]]),Add(c.y[1],b.x[a.p[1]]);
if(!a.rev[2])Add(c.x[2],b.x[a.p[2]]),Add(c.y[2],b.y[a.p[2]]);
else Add(c.x[2],b.y[a.p[2]]),Add(c.y[2],b.x[a.p[2]]);
c.p[0]=b.p[a.p[0]];
c.p[1]=b.p[a.p[1]];
c.p[2]=b.p[a.p[2]];
return c;
}
};
node f[MXK+5];
inline void Solve(){
io>>n>>k;
node L=node(0,0,0,0,n-1,0,0,0,0,1,0,1,0,1,2);
node R=node(0,0,0,0,1,0,0,0,0,n-1,0,n-1,0,1,2);
node U=node(0,0,0,0,0,n-1,1,1,0,0,0,0,0,1,2);
node D=node(0,0,0,0,0,1,n-1,n-1,0,0,0,0,0,1,2);
node I=node(0,0,1,0,0,0,0,0,0,0,0,0,1,0,2);
node C=node(0,1,0,0,0,0,0,0,0,0,0,0,2,1,0);
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)io>>m[0][i][j];
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)m[1][i][m[0][i][j]-1]=j+1;
for(int j=0;j<n;j++)
for(int i=0;i<n;i++)m[2][m[0][i][j]-1][j]=i+1;
io>>op;
f[0]=node();
for(int i=1;i<=k;i++){
if(op[i-1]=='R')f[i]=f[i-1]*R;
else if(op[i-1]=='L')f[i]=f[i-1]*L;
else if(op[i-1]=='D')f[i]=f[i-1]*D;
else if(op[i-1]=='U')f[i]=f[i-1]*U;
else if(op[i-1]=='I')f[i]=f[i-1]*I;
else f[i]=f[i-1]*C;
}
node now=f[k];
int pos=0;
for(int i=0;i<3;i++)if(now.p[i]==0)pos=i;
for(int x=0;x<n;x++,io<<'\n'){
for(int y=0;y<n;y++){
short X=now.x[pos],Y=now.y[pos];
if(now.rev[pos])Add(X,y),Add(Y,x);
else Add(Y,y),Add(X,x);
int tmp=Sum(m[pos][X][Y],now.tag[pos]);
if(tmp>n)tmp-=n;
if(tmp==0)tmp=n;
io<<tmp<<' ';
}
}
}
int main(){
io>>T;
while(T--)Solve();
return 0;
}