题解 - B3761 [信息与未来 2021] 三角魔方
B3761 [信息与未来 2021] 三角魔方
题目大意
对于一个三角魔方,给定一个操作序列和两个参数
题目分析
显而易见,这是一道模拟题,我们可以直接模拟魔方的操作。暴力写法可以获得
约定用 0-index 的字符串 `ABCDEFGHIJKLMNOP`。
显然,
我们不妨以操作
原先的
swap(s[4], s[10]);
swap(s[10], s[11]);
其他操作同理。注意每个操作的方向和顺序,实在不理解的可以自己尝试画图。
:::success[参考代码]
void change (string &s, char c, int u) {
if (u == 1) return;
if (c == 'R') {
if (u == 3)
for (int i = 3; i > 1; --i) swap(s[i], s[i - 1]); // 由于操作对象都在同一行,可以使用循环简化代码
if (u == 5)
for (int i = 8; i > 4; --i) swap(s[i], s[i - 1]);
if (u == 7)
for (int i = 15; i > 9; --i) swap(s[i], s[i - 1]);
}
if (c == 'U') {
if (u == 3) {
swap(s[4], s[10]); swap(s[10], s[11]);
}
if (u == 5) {
swap(s[1], s[5]); swap(s[5], s[6]);
swap(s[6], s[12]); swap(s[12], s[13]);
}
if (u == 7) {
swap(s[0], s[2]); swap(s[2], s[3]);
swap(s[3], s[7]); swap(s[7], s[8]);
swap(s[8], s[14]); swap(s[14], s[15]);
}
}
if (c == 'D') {
if (u == 3) {
swap(s[13], s[14]); swap(s[14], s[8]);
}
if (u == 5) {
swap(s[11], s[12]); swap(s[12], s[6]);
swap(s[6], s[7]); swap(s[7], s[3]);
}
if (u == 7) {
swap(s[9], s[10]); swap(s[10], s[4]);
swap(s[4], s[5]); swap(s[5], s[1]);
swap(s[1], s[2]); swap(s[2], s[0]);
}
}
}
:::
但是有个问题:注意到
魔方的操作结果会呈周期循环出现,在本题中也是如此。以下是简要证明:
:::info[证明]
我们可以用抽屉原理来严格证明:
-
构造一个状态序列:从初始状态
S_0 开始,连续执行同一个操作序列T 。我们得到一个无限长的状态序列:S_0, S_1 = T(S_0), S_2 = T(S_1), \dots 。 -
应用抽屉原理:由于状态总数只有有限的
16! 种,但这个序列可以无限延长。因此,在这个无限序列中,必然至少有两个状态是相同的。也就是说,存在i < j ,使得S_i = S_j 。 -
确定循环起点:因为操作
T 是可逆的(有逆操作),我们可以从S_i = S_j 这个等式出发。对等式两边同时施加i 次逆操作,就能得到S_0 = S_{j-i} 。 -
结论:这意味着,从初始状态出发,经过
j-i 次操作后,状态首次回到了初始状态S_0 。从此以后,状态序列就会以j-i 为周期,在S_0, S_1, \dots, S_{j-i-1} 这些状态之间无限循环下去。
:::
所以,我们可以求出魔方在第一次回到初始状态时的操作次数
需要前置知识——快速幂。
附上代码:
:::success[AC Code]
// OwO
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define st first
#define nd second
string S = "ABCDEFGHIJKLMNOP"; // 初始序列,用于对比
string T = "ABCDEFGHIJKLMNOP"; // 实际被操作序列
string op; // 操作序列
int a, n, mod = 0;
void change (string &s, char c, int u);
int fpow (int a, int n);
signed main () {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> op >> a >> n;
while (true) {
for (int i = 0; i < op.size(); i += 2) {
change(T, op[i], op[i + 1] - '0');
}
mod ++;
if (T == S) break;
}
int k = fpow(a, n);
while (k--) {
for (int i = 0; i < op.size(); i += 2) {
change(T, op[i], op[i + 1] - '0');
}
}
cout << T << endl;
return 0;
}
void change (string &s, char c, int u) {
if (u == 1) return;
if (c == 'R') {
if (u == 3)
for (int i = 3; i > 1; --i) swap(s[i], s[i - 1]);
if (u == 5)
for (int i = 8; i > 4; --i) swap(s[i], s[i - 1]);
if (u == 7)
for (int i = 15; i > 9; --i) swap(s[i], s[i - 1]);
}
if (c == 'U') {
if (u == 3) {
swap(s[4], s[10]); swap(s[10], s[11]);
}
if (u == 5) {
swap(s[1], s[5]); swap(s[5], s[6]);
swap(s[6], s[12]); swap(s[12], s[13]);
}
if (u == 7) {
swap(s[0], s[2]); swap(s[2], s[3]);
swap(s[3], s[7]); swap(s[7], s[8]);
swap(s[8], s[14]); swap(s[14], s[15]);
}
}
if (c == 'D') {
if (u == 3) {
swap(s[13], s[14]); swap(s[14], s[8]);
}
if (u == 5) {
swap(s[11], s[12]); swap(s[12], s[6]);
swap(s[6], s[7]); swap(s[7], s[3]);
}
if (u == 7) {
swap(s[9], s[10]); swap(s[10], s[4]);
swap(s[4], s[5]); swap(s[5], s[1]);
swap(s[1], s[2]); swap(s[2], s[0]);
}
}
}
int fpow (int a, int n) {
if (n == 0) return 1;
int x = fpow(a, n / 2);
return x * x % mod * (n % 2 ? a : 1) % mod;
}
:::