题解:P4847 银河英雄传说V2
题意
给定
个长度为 的序列,第 个序列中有一个元素,值为 ,接下来有三种操作:
M x y
,表示把所在的序列放到 所在的序列之后。如果 已经在同一个序列,则不进行操作。 D x
,表示把所在的序列中从 处断开,也就是把 及 之后的元素单独取出来作为一个序列。 Q x y
,表示查询到 之间(包括 和 )所有元素的值之和。如果 和 不在同一个序列之中,输出 -1
。
分析
看到操作有分裂以及合并,考虑使用 FHQ Treap。
但是发现普通的 FHQ Treap 好像不支持按节点分裂。
所以我们对它进行一些修改,让节点额外维护它的父亲节点。
考虑如何维护父亲节点。
我们可以在 push_up()
中维护父亲节点,将两个子节点的父节点设为自己。
node* push_up() { siz=1; sum=v; if(lc) siz+=lc->siz, sum+=lc->sum, lc->fa=this; if(rc) siz+=rc->siz, sum+=rc->sum, rc->fa=this; return this; }
但是分裂后的两根节点没有被更新,可能保存有错误的父节点指针。
分裂后或者合并后我们手动将它的父亲节点设为
维护了父亲节点,我们可以很容易地得到节点在中序遍历中的位置:
uint32_t find_pos(node *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 *find_fa(node *x) { while(x->fa) x=x->fa; return x; }
剩下的操作就很简单了,都是 FHQ Treap 基本操作。
Code
#include<bits/stdc++.h> using namespace std; #define maxn 200005 namespace Treap { mt19937 rnd(time(0)); struct node { int64_t v; uint64_t id; int64_t siz=1; node *lc=0, *rc=0, *fa=0; int64_t add=0, sum=0; node(int va, uint64_t d): v(va), id(d) {sum=v;} void push_down() { if(!add) return; if(lc) lc->sum+=add*lc->siz, lc->add+=add, lc->v+=add; if(rc) rc->sum+=add*rc->siz, rc->add+=add, rc->v+=add; add=0; } node* push_up() { siz=1; sum=v; if(lc) siz+=lc->siz, sum+=lc->sum, lc->fa=this; if(rc) siz+=rc->siz, sum+=rc->sum, rc->fa=this; return this; } }; node* new_node(int 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(); } } node *find_fa(node *x) { while(x->fa) x=x->fa; return x; } uint32_t find_pos(node *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; } } Treap::node *rt[maxn]; int main() { int n, m; cin>>n>>m; for(int i=1, t;i<=n;i++) cin>>t, rt[i]=Treap::new_node(t); while(m--) { char op; int x, y; cin>>op>>x; if(op=='M') { cin>>y; auto fx=Treap::find_fa(rt[x]); auto fy=Treap::find_fa(rt[y]); if(fy==fx) continue; merge(fy, fx)->fa=0; } if(op=='D') { auto fx=Treap::find_fa(rt[x]); uint32_t rnk=Treap::find_pos(rt[x]); Treap::node *a, *b; Treap::split(fx, rnk-1, a, b); if(a) a->fa=0; if(b) b->fa=0; } if(op=='Q') { cin>>y; auto fx=Treap::find_fa(rt[x]); auto fy=Treap::find_fa(rt[y]); if(fx!=fy) cout<<-1<<'\n'; else { uint32_t l=Treap::find_pos(rt[x]); uint32_t r=Treap::find_pos(rt[y]); if(l>r) swap(l, r); Treap::node *a, *b, *c; Treap::split(fx, l-1, a, b); Treap::split(b, r-l+1, b, c); int64_t ans=b->sum; merge(merge(a, b), c)->fa=0; cout<<ans<<'\n'; } } } }
本文作者:Jimmy-LEEE
本文链接:https://www.cnblogs.com/redacted-area/p/18379557
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步