1 条题解

  • 0
    @ 2026-10-31 17:57:28

    思路

    题意:给出一个 n×nn\times n 的“阶梯型”矩阵(每行右侧是一段连续的 0,且向下非零元素个数不减)。每次操作是:整体顺时针旋转 90°,再让每列的石块受重力落到底部。问是否存在正整数 tt,使矩阵经过 tt 次操作后回到原样。

    结论

    答案是恒为 YES:任何合法输入都一定会在有限次操作后复原。因此程序只需读入矩阵并输出 YES。

    证明:一次操作是可逆的双射

    记一次操作为 ff。关键是证明 ff 在状态集合上是双射。

    状态集合有限。 旋转只是重排格子,重力只是把每列的非零元素在列内下移,二者都不改变整个矩阵中元素的多重集合(方块编号和 0 的个数都不变)。因此所有可能出现的状态都包含在“给定多重集合在 n2n^2 个格子上的排列”这一有限集合 SS 中。

    ff 可逆。 给定操作结果 bb,可以唯一地一步步倒推:

    1. 重力作用后,每列的非零元素必然聚在列的底部、且保持它们在重力前的相对顺序(下落的石块不会互相穿越)。于是把每列底部那段非零元素整体搬回该列顶部、其余位置补 0,就唯一还原出“只旋转、未受重力”的矩阵 vv;
    2. 对 vv 逆时针旋转 90°,就唯一还原出操作前的矩阵 aa。

    倒推过程对每个 bb 至多给出一组 (v,a)(v,a),即 ff 是单射。有限集合上的单射必为双射,从而 ff 是整个 SS 上的一个置换。

    由双射推出周期性

    置换可以分解为若干互不相交的循环。初始矩阵落在其中一个循环上,沿着 ff 迭代,循环长度 tt 次后必然回到自身,故一定存在正整数 tt 使矩阵复原。

    顺带一提,题目保证输入满足阶梯型,但实际上上面的论证只需要矩阵元素的多重集合固定,所以对任何输入都成立;也正因为如此,代码不需要保存或使用矩阵内容。

    实现与复杂度

    go() 中读入 nn,再用两层循环读掉 n2n^2 个整数(读完即丢),最后输出 YES。时间 O(n2)O(n^2)(受读入规模限制),额外空间 O(1)O(1);n≤1000n \le 1000 时读入量约 10610^6 个整数,关闭流同步后完全够快。

    代码

    #include<bits/stdc++.h>
    #define int long long
    #define dd double
    #define str string
    #define el cout << '\n'
    #define re return
    #define ff first
    #define ss second
    #define pll pair<int,int>
    #define pdd pair<dd,dd>
    #define all(x) x.begin(),x.end()
    #define pb push_back
    using namespace std;
    
    // 每组数据:读入矩阵后直接输出结论
    void go(){
        int n; cin >> n;
        // 结论与矩阵内容无关,只需把输入读掉,不必保存
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                int x;cin>>x;
            }
        }
    
        cout<<"YES";el;    // 任意合法矩阵都会在有限次操作后复原
    }
    
    
    signed main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);cout.tie(nullptr);
        int _=1;
        // 本题只有一组数据;若需要多组输入,应在此处读入组数 T
        while(_--){
            go();
        }
        return 0;
    }
    
    • 1

    信息

    ID
    45
    时间
    1000ms
    内存
    256MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者