考虑从低到高位建立 3 3 3进制的 trie \text{trie} trie树。然后操作 1 1 1相当于交换每个节点的 1 1 1号儿子和 2 2 2号儿子,当然我们不会老老实实对每个结点交换它的儿子,而是通过打懒标来解决。这也契合了我们只需要输出最终序列的目的。
真正令人头疼的是操作 2 2 2。首先对于根节点,我们应该把 1 1 1号儿子接到 2 2 2号儿子上面, 2 2 2号儿子接到 3 3 3号儿子上面,对于 3 3 3号儿子,我们又要把对应的子树拼接到 0 0 0号儿子对应的位置,我们只用把整颗子树搬过去而不用对子树内部进行额外的递归,因此每次只会递归一边。
复杂度 O ( ∣ T ∣ n + 3 n ) O(|T|n+3^n) O(∣T∣n+3n)。
最近似乎总是遇到以前的知识点,但是理解不深入又不会做的情况,看来还是太菜了
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
int n,f[13],res[1000005],tot=1;
string s;
struct trie{
int son[3];
int val,lazy;
}t[1000005];
int get(int x,int y){
return x/f[y]%3;
}
void ins(int x){
int it=1;
for(int i=0;i<n;i++){
int p=get(x,i);
if(!t[it].son[p])t[it].son[p]=++tot;
it=t[it].son[p];
}t[it].val=x;
}
void pushdown(int p){
if(t[p].lazy){
swap(t[p].son[1],t[p].son[2]);
t[t[p].son[0]].lazy^=1,t[t[p].son[1]].lazy^=1,t[t[p].son[2]].lazy^=1;
t[p].lazy=0;
}
}
void change(int p){
if(!p)return;
pushdown(p);
int x=t[p].son[0],y=t[p].son[1],z=t[p].son[2];
t[p].son[1]=x,t[p].son[2]=y,t[p].son[0]=z;
change(z);
}
int qry(int x){
int it=1;
for(int i=0;i<n;i++){
int p=get(x,i);
pushdown(it),it=t[it].son[p];
}return t[it].val;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>s;f[0]=1;for(int i=1;i<=12;i++)f[i]=f[i-1]*3;
for(int i=0;i<f[n];i++)ins(i);
for(int i=0;i<s.size();i++){
if(s[i]=='S'){
t[1].lazy^=1;
}
else{
change(1);
}
}for(int i=0;i<f[n];i++)res[qry(i)]=i;
for(int i=0;i<f[n];i++)cout<<res[i]<<' ';
}
想了一下,发现我只会 O ( n 4 ) O(n^4) O(n4)的暴力做法。想了想剪枝,感觉过不了,似乎网格图也没啥性质。
然后就自闭了
这题看起来很像一个搜索题,然而又是一个算贡献的题目,那么我们考虑所有点的答案之和加起来,应该是 n 3 n^3 n3级别。