1 条题解
-
0
思路
题意:给出一个 的“阶梯型”矩阵(每行右侧是一段连续的 0,且向下非零元素个数不减)。每次操作是:整体顺时针旋转 90°,再让每列的石块受重力落到底部。问是否存在正整数 ,使矩阵经过 次操作后回到原样。
结论
答案是恒为 YES:任何合法输入都一定会在有限次操作后复原。因此程序只需读入矩阵并输出
YES。证明:一次操作是可逆的双射
记一次操作为 。关键是证明 在状态集合上是双射。
状态集合有限。 旋转只是重排格子,重力只是把每列的非零元素在列内下移,二者都不改变整个矩阵中元素的多重集合(方块编号和 0 的个数都不变)。因此所有可能出现的状态都包含在“给定多重集合在 个格子上的排列”这一有限集合 中。
可逆。 给定操作结果 ,可以唯一地一步步倒推:
- 重力作用后,每列的非零元素必然聚在列的底部、且保持它们在重力前的相对顺序(下落的石块不会互相穿越)。于是把每列底部那段非零元素整体搬回该列顶部、其余位置补 0,就唯一还原出“只旋转、未受重力”的矩阵 ;
- 对 逆时针旋转 90°,就唯一还原出操作前的矩阵 。
倒推过程对每个 至多给出一组 ,即 是单射。有限集合上的单射必为双射,从而 是整个 上的一个置换。
由双射推出周期性
置换可以分解为若干互不相交的循环。初始矩阵落在其中一个循环上,沿着 迭代,循环长度 次后必然回到自身,故一定存在正整数 使矩阵复原。
顺带一提,题目保证输入满足阶梯型,但实际上上面的论证只需要矩阵元素的多重集合固定,所以对任何输入都成立;也正因为如此,代码不需要保存或使用矩阵内容。
实现与复杂度
go()中读入 ,再用两层循环读掉 个整数(读完即丢),最后输出YES。时间 (受读入规模限制),额外空间 ; 时读入量约 个整数,关闭流同步后完全够快。代码
#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
- 上传者