题解:P4847 银河英雄传说V2

题意

给定 n 个长度为 1 的序列,第 i 个序列中有一个元素,值为 ai,接下来有三种操作:

  1. M x y,表示把 x 所在的序列放到 y 所在的序列之后。如果 x,y 已经在同一个序列,则不进行操作。
  2. D x,表示把 x 所在的序列中从 x 处断开,也就是把 xx 之后的元素单独取出来作为一个序列。
  3. Q x y,表示查询 xy 之间(包括 xy)所有元素的值之和。如果 xy 不在同一个序列之中,输出 -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;
}

但是分裂后的两根节点没有被更新,可能保存有错误的父节点指针。

分裂后或者合并后我们手动将它的父亲节点设为 0 即可。

维护了父亲节点,我们可以很容易地得到节点在中序遍历中的位置:

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 中国大陆许可协议进行许可。

posted @   Jimmy-LEEE  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起