abc 243 d 题解
abc 243 d
题意
有一个有着 \(2 ^ {10 ^ {100}} - 1\) 个结点的完全二叉树,每个结点编号为 \(1, 2, \dots, 2 ^ {10 ^ {100}} - 1\)。\(1\) 号结点是根结点,对于 \(1 \le i < 2 ^ {10 ^ {100}} - 1\) 中的每个 \(i\),结点 \(i\) 有两个子节点:左儿子 \(2i\) 和右儿子 \(2i + 1\)。
Takahashi 从顶点 \(X\) 开始执行 \(N\) 次移动,用字符串 \(S\) 表示。第 \(i\) 次移动如下:
-
如果 \(S\) 的第 \(i\) 个字符为
U
,则转到他现在所在顶点的父节点。 -
如果 \(S\) 的第 \(i\) 个字符为
L
,则转到他现在所在顶点的左孩子。 -
如果 \(S\) 的第 \(i\) 个字符为
R
,则转到他现在所在顶点的右孩子。
找到 \(N\) 次移动后 Takahashi 所在的结点编号,保证答案最多为 \(10 ^ {18}\)。
思路
暴力
若当前是第 \(i\) 次操作,并且在 \(A\) 号结点上,那么:
-
如果 $S_i = $
U
,\(A \rightarrow \lfloor \frac{A}{2} \rfloor\)。 -
如果 $S_i = $
L
,\(A \rightarrow 2 \times A\)。 -
如果 $S_i = $
R
,\(A \rightarrow 2 \times A + 1\)。
最后输出即可。
正解
由于题目并没有保证在移动的过程中的结点编号最多为 \(10 ^ {18}\),所以,肯定不能直接模拟。
做法 1
栈。
如果先做了 L
或 R
操作,再做了 U
操作,实际上还是在这个点上。
那么,我们只要将所有的 L
和 R
存入栈中,每次遇到一个 U
就弹出栈顶即可。
最后,将剩下的操作全部模拟即可。
时间复杂度为 \(O(N)\)。
做法 2
二进制。
先将 \(X\) 转成二进制,用一个 01 串 \(A\) 表示。
若当前是第 \(i\) 次操作,那么:
-
如果 $S_i = $
U
,\(A\) 的长度减少 1。 -
如果 $S_i = $
L
,在 \(A\) 的末尾加上 \(0\)。 -
如果 $S_i = $
R
,在 \(A\) 的末尾加上 \(1\)。
时间复杂度为 \(O(N + \log X)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
int n, x[N], len;
string s;
int top, stk[N];
void F() {
long long p;
cin >> p;
while (p) {
x[len++] = p % 2, p /= 2;
}
for (int i = len - 1; i >= 0; i--) {
stk[++top] = x[i];
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
F(); // 转二进制
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (s[i] == 'U') {
top--;
} else if (s[i] == 'L') {
stk[++top] = 0;
} else {
stk[++top] = 1;
}
}
long long ans = 0;
for (long long i = 1; top; top--, i *= 2) {
ans += stk[top] * i; // 转回十进制
}
cout << ans;
return 0;
}