题意
有 \(3^n\) 个人在绕圈圈跳舞。我们从任意一个人开始,给这些人环绕标号,记为 \(0,1,\dots,3^n-1\)。
现在有两种舞 :
- Salasa 舞。所有人走向其对应的位置,位于位置 \(i\) 的人走向第 \(j\) 个位置当且仅当 \(i,j\) 的三进制表示,\(1\) 对应 \(2\),\(2\) 对应 \(1\)。比如,\((46)_{10}=(1201)_3\) 可以走向 \((65)_{10}=(2102)_3\),当然 \(65\) 也走向 \(46\)。
- Rumba 舞。所有人走向其所在位置编号加 \(1\) 的位置。如果等于 \(3^n−1\),去 \(0\)。
现在给你一个 \(T\) 序列表示以上两种舞。请问最后每个人站在哪里?
分析
从低位到高位建 \(Trie\),维护 \(w[p]\) 表示位置为根结点到结点 \(p\) 的路径所表示的三进制数的是第几个人。
处理 \(S\) 也就是实现交换 \(trie[p][1]\) 和 \(trie[p][2]\),需要实现懒标记进行延迟交换。
处理 \(T\) 也就是全局加一,三进制就是让 \(trie[p][(z+1)\bmod 3]=trie[p][z]\)。
时间复杂度 \(O(3^n+n|T|)\)
代码
#include <iostream>
#include <string>
using namespace std;
const int N = 800011;
string T;
int rt, n, len, tot, three[21], trie[N][3], w[N], lazy[N], ans[N];
void build(int &rt, int dep, int now) {// 建 Trieif (!rt)rt = ++tot;if (dep == n) {w[rt] = now;return;}for (int i = 0; i < 3; ++i)build(trie[rt][i], dep + 1, now + i * three[dep]);
}
void plusone(int rt) {// 全局加一if (!trie[rt][0])return;if (lazy[rt]) {// 延迟标记for (int i = 0; i < 3; ++i)lazy[trie[rt][i]] ^= 1;swap(trie[rt][1], trie[rt][2]);lazy[rt] = 0;}int t = trie[rt][0];trie[rt][0] = trie[rt][2];trie[rt][2] = trie[rt][1];trie[rt][1] = t;plusone(trie[rt][0]);
}
void dfs(int rt, int dep, int now) {if (dep == n) {ans[w[rt]] = now;return;}if (lazy[rt]) {for (int i = 0; i < 3; ++i)lazy[trie[rt][i]] ^= 1;swap(trie[rt][1], trie[rt][2]);lazy[rt] = 0;}for (int i = 0; i < 3; ++i)dfs(trie[rt][i], dep + 1, now + i * three[dep]);
}
int main() {ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);cin >> n >> T, len = T.length();three[0] = 1;for (int i = 1; i <= n; ++i)three[i] = three[i - 1] * 3;build(rt, 0, 0);for (int i = 0; i < len; ++i)if (T[i] == 'S')lazy[rt] ^= 1;elseplusone(rt);dfs(rt, 0, 0);for (int i = 0; i < three[n]; ++i)cout << ans[i] << ' ';return 0;
}