#Trie#AGC044 C - Strange Dance
题意
有 \(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) {// 建 Trie
if (!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;
else
plusone(rt);
dfs(rt, 0, 0);
for (int i = 0; i < three[n]; ++i)
cout << ans[i] << ' ';
return 0;
}

浙公网安备 33010602011771号