题解:P5217 贫穷
题意
维护一个字符串,支持以下操作:
,在第 个字母后面插入一个 。 ,删除第 个字母。 ,反转当前文本中的区间 。 ,输出初始文本中第 个字母在当前文本中的位置。特别地,若不存在,输出 。 ,输出当前文本中第 个字母。 ,输出当前文本中区间 内出现过的字母的种类数。
分析
序列问题,有插入操作,直接无脑上平衡树。
本题解使用 FHQ Treap。
我们先看这一个操作:
,输出初始文本中第 个字母在当前文本中的位置。特别地,若不存在,输出 。
发现一般的 FHQ Treap 无法维护相关信息。
考虑记录每个节点的父亲节点。
从该节点向根跳,如果该节点是父亲节点的右儿子,那么记录父亲节点及其左子树贡献。
新的问题来了:如何维护父亲节点?
我们可以考虑在 push_up()
操作时维护,将两个子节点的父节点设为当前节点。
对于个别节点(split()
或 merge()
操作后的根节点),在操作完后单独将父节点设为空即可。
该部分代码如下:
void access(node *x) { if(!x) return; access(x->fa); x->push_down(); } uint32_t find_pos(node *x) { if(x->del||!x) return 0; access(x); uint32_t res=siz(x->lc)+1; while(x->fa) { if(x->fa->rc==x) res+=siz(x->fa->lc)+1; x=x->fa; } return res; }
然后来看查询操作:
,输出当前文本中区间 内出现过的字母的种类数。
可以维护一个 bitset
,记录哪些字母是出现过的。
push_up()
代码如下:
node* push_up() { siz=1; chr.reset(); chr[c-'a']=1; if(lc) siz+=lc->siz, chr|=lc->chr, lc->fa=this; if(rc) siz+=rc->siz, chr|=rc->chr, rc->fa=this; return this; }
查询本身就很套路了:
int query(int l, int r) { node *a, *b, *c; split(str, l-1, a, b); split(b, r-l+1, b, c); int ret=b->chr.count(); str=merge(merge(a, b), c); str->fa=0; return ret; }
其他的都是 FHQ Treap 的基本操作,不多赘述。
Code
#include<bits/stdc++.h> using namespace std; #define maxn 200005 namespace Treap { mt19937 rnd(time(0)); struct node { uint64_t id; int64_t siz=1; node *lc=0, *rc=0, *fa=0; char c; bool del=0, rev=0; bitset<26> chr; node(char ch, uint64_t d): c(ch), id(d) {chr.reset(); chr[ch-'a']=1;} void reverse() {swap(lc, rc); rev^=1;} void push_down() { if(rev) { if(lc) lc->reverse(); if(rc) rc->reverse(); rev^=1; } } node* push_up() { siz=1; chr.reset(); chr[c-'a']=1; if(lc) siz+=lc->siz, chr|=lc->chr, lc->fa=this; if(rc) siz+=rc->siz, chr|=rc->chr, rc->fa=this; return this; } }; node* new_node(char v) {return new node(v, rnd());} #define siz(x) (x?x->siz:0) void split(node *x, int s, node *&l, node *&r) { if(!x) return l=r=0, void(); x->push_down(); if(siz(x->lc)<s) l=x, split(x->rc, s-siz(x->lc)-1, x->rc, r); else r=x, split(x->lc, s, l, x->lc); x->push_up(); } node* merge(node *x, node *y) { if(!x||!y) return x?x:y; if(x->id<y->id) { x->push_down(); x->rc=merge(x->rc, y); return x->push_up(); } else { y->push_down(); y->lc=merge(x, y->lc); return y->push_up(); } } void access(node *x) { if(!x) return; access(x->fa); x->push_down(); } uint32_t find_pos(node *x) { if(x->del||!x) return 0; access(x); uint32_t res=siz(x->lc)+1; while(x->fa) { if(x->fa->rc==x) res+=siz(x->fa->lc)+1; x=x->fa; } return res; } node *str; void insert(int p, char c) { node *a, *b; split(str, p, a, b); str=merge(merge(a, new_node(c)), b); str->fa=0; } void erase(int p) { node *a, *b, *c; split(str, p-1, a, b); split(b, 1, b, c); b->del=1; str=merge(a, c); if(str) str->fa=0; } void reverse(int l, int r) { node *a, *b, *c; split(str, l-1, a, b); split(b, r-l+1, b, c); b->reverse(); str=merge(merge(a, b), c); str->fa=0; } char get(int p) { node *a, *b, *c; split(str, p-1, a, b); split(b, 1, b, c); char ret=b->c; str=merge(merge(a, b), c); str->fa=0; return ret; } int query(int l, int r) { node *a, *b, *c; split(str, l-1, a, b); split(b, r-l+1, b, c); int ret=b->chr.count(); str=merge(merge(a, b), c); str->fa=0; return ret; } } Treap::node *rt[maxn]; string s; int main() { int n, m; cin>>n>>m>>s; for(int i=1;i<=n;i++) rt[i]=Treap::new_node(s[i-1]), Treap::str=merge(Treap::str, rt[i]); while(m--) { char op; int x, y; char ch; cin>>op>>x; if(op=='I') cin>>ch, Treap::insert(x, ch); if(op=='D') Treap::erase(x); if(op=='R') cin>>y, Treap::reverse(x, y); if(op=='P') cout<<Treap::find_pos(rt[x])<<'\n'; if(op=='T') cout<<Treap::get(x)<<'\n'; if(op=='Q') cin>>y, cout<<Treap::query(x, y)<<'\n'; } }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379546
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步