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; 
}
posted @ 2025-03-12 07:54  KIreteria  阅读(7)  评论(0)    收藏  举报