B4532 信息与未来 2026 折纸状态维护

折纸:只盯住一格

这节课不模拟整张纸。我们只维护目标格子的当前位置、层号、每叠层数和朝向,用 5-10 分钟把“翻折反转、保留加压”讲清楚。

(1,1)
目标格子是否被翻折?

一、先回答问题

一句话模型

每次只判断左上角那个格子在被翻折的一半,还是留在原地的一半。

它被翻折:这一小叠上下顺序反转,正反面翻转。它没被翻折:另一半会压到它上面,层号加上一整叠。

不要做什么

不要开数组模拟全部格子。最大边长是 215,最终层数是 230,完整模拟没有必要。

我们维护 5 个量:h, w, r, c, layer, sz, up。其中 sz 是当前一个位置叠了多少层。

二、核心观察

1. 位置会缩小

纵向折叠后高度减半,横向折叠后宽度减半。目标格子的 r,c 要映射到新的纸面坐标。

2. 被翻折

如果目标在折过去的那半边:layer = sz - layer + 1,并且 up = !up

3. 被压住

如果目标留在原地那半边:翻过来的整叠在上方,目标层号变为 layer += sz

四个方向怎么判
  • D:上半向下。目标在上半就是被翻折;在下半就是被压住。
  • U:下半向上。目标在下半就是被翻折;在上半就是被压住。
  • R:左半向右。目标在左半就是被翻折;在右半就是被压住。
  • L:右半向左。目标在右半就是被翻折;在左半就是被压住。

三、互动演示:样例 DR / DRUR

当前纸面 准备开始
目标位置 (0, 0)
当前尺寸 4 x 4
目标层号 1
朝向 U
每格层数 sz 1
已处理 0 / 4
本步解释 点击“下一折”,观察目标格子是否位于被翻折的一半。
课堂读法:先问“目标在哪半边”,再决定是 layer 反转还是 layer 加 sz。不要先想整张纸最终长什么样。

四、自己选答案

问题 1:当前每格有 sz=8 层,目标层号是 3。如果目标格子被翻折,新层号是多少?

问题 2:做一次 L 折,目标在左半边。它是被翻折,还是被压住?

问题 3:为什么每次处理完都要 sz *= 2

五、手推 DRUR

步骤 方向 目标判断 层号与朝向 尺寸 / sz
初始 - 左上角,位置 (0,0) layer=1,U 4x4,sz=1
1 D 在上半,被翻折 1 反转仍为 1,朝向变 D 2x4,sz=2
2 R 在左半,被翻折 layer = 2 - 1 + 1 = 2,朝向变 U 2x2,sz=4
3 U 在下半,被翻折 layer = 4 - 2 + 1 = 3,朝向变 D 1x2,sz=8
4 R 在右半,被压住 layer = 3 + 8 = 11,朝向仍 D 1x1,sz=16

六、C++14 参考代码

本地 tok-* 高亮,无 CDN
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    string s;
    cin >> n >> s;

    ll h = 1LL << n, w = 1LL << n;
    ll r = 0, c = 0;      // 目标格子当前坐标,0-based
    ll layer = 1;         // 从上往下数的层号
    ll sz = 1;            // 当前每个位置有多少层
    bool up = true;       // 正面是否朝上

    for (char ch : s) {
        if (ch == 'D') {
            ll half = h / 2;
            if (r < half) {              // 上半被翻到下半上方
                r = half - 1 - r;
                layer = sz - layer + 1;
                up = !up;
            } else {
                r -= half;
                layer += sz;
            }
            h = half;
        } else if (ch == 'U') {
            ll half = h / 2;
            if (r >= half) {             // 下半被翻到上半上方
                r = h - 1 - r;
                layer = sz - layer + 1;
                up = !up;
            } else {
                layer += sz;
            }
            h = half;
        } else if (ch == 'R') {
            ll half = w / 2;
            if (c < half) {              // 左半被翻到右半上方
                c = half - 1 - c;
                layer = sz - layer + 1;
                up = !up;
            } else {
                c -= half;
                layer += sz;
            }
            w = half;
        } else {
            ll half = w / 2;
            if (c >= half) {             // 右半被翻到左半上方
                c = w - 1 - c;
                layer = sz - layer + 1;
                up = !up;
            } else {
                layer += sz;
            }
            w = half;
        }
        sz *= 2;
    }

    cout << layer << ' ' << (up ? 'U' : 'D') << '\n';
    return 0;
}
  • 代码里每个方向都只有两类情况:目标被翻折,或目标留在原半边被压住。
  • layer = sz - layer + 1 只在“目标所在的小叠本身翻过来”时发生。
  • long long 足够容纳最终层号,因为最大层数是 2^30

七、常见错误

漏掉被压住

目标没有被翻折不等于层号不变。另一半翻到它上面时,要 layer += sz

坐标没归一

D/R 中目标在保留半边时,坐标要减去 half,映射到新纸面。

朝向乱翻

只有目标格子所在的小叠被整体翻折时,朝向才会反转。

课堂节奏建议:先推 DR,再推 DRUR,最后让学生口答 L/U 的“被翻折还是被压住”。