银河英雄传说V2 题解
主要思想:合并平衡树!
(以下所有代码内容中 key 代表该节点权值,val 代表键值)
初始状态下我们可以把每个节点作为一个平衡树,接下来按照题意进行合并即可(这点其他题解说的很清楚。)
因为如果记录每一个平衡树的根的话非常难维护,所以我们不妨不记录每棵树的根节点,当用到根节点的时候直接现找。
为了更方便的找一棵树的根和某个节点的位置以及其他更多信息,我们记录每个节点的父节点。
- 如何更新父节点:
在 push up 的时候顺带上传即可:
inline void pushup(ll pos) {
tree[pos].siz=tree[tree[pos].l].siz+tree[tree[pos].r].siz+1;
tree[pos].sum=tree[tree[pos].l].sum+tree[tree[pos].r].sum+tree[pos].key;
if (tree[pos].l) tree[tree[pos].l].fa=pos;
if (tree[pos].r) tree[tree[pos].r].fa=pos;
}
split之后每个节点的父节点需要重新初始化如下:
inline void split(ll pos,ll x,ll &l,ll &r) {
if (!pos) {l=r=0;return;}
ll u=tree[tree[pos].l].siz+1;
if (u<=x) {
l=pos;
split(tree[l].r,x-u,tree[l].r,r);
}
else {
r=pos;
split(tree[r].l,x,l,tree[r].l);
}
tree[l].fa=l,tree[r].fa=r;
pushup(pos);
}
- 如何通过父节点信息找到该节点对应树的根:
inline ll getfa(ll pos) {
while (tree[pos].fa!=pos) pos=tree[pos].fa;
return pos;
}
- 如何通过父节点找到该节点处在该树的位置:
inline ll getrank(ll pos) {
ll cnt=tree[tree[pos].l].siz+1;
while (tree[pos].fa!=pos) {
if (tree[tree[pos].fa].r==pos) cnt+=tree[tree[tree[pos].fa].l].siz+1;
pos=tree[pos].fa;
}
return cnt;
}
- 以下内容为一些初学的人设计,目的以理解平衡树
merge的本质。
接下来介绍一道和这题很像但不一样的题:[HNOI2012] 永无乡。
这道题的思路也是平衡树合并,但是这道题合并时需要启发式合并,为什么现在不用?
这就要说到两种 split 的方式了:
我们都知道一般 split 有这两种形式:
- 第一种:
ll u=tree[tree[pos].l].siz+1;
if (u<=x) {
l=pos;
split(tree[pos].r,x-u,tree[pos].r,r);
pushup(l);
}
- 第二种:
if (tree[pos].key<=x) {
l=pos;
split(tree[l].r,x,tree[l].r,r);
}
因为[HNOI2012] 永无乡中节点维护的是该数的大小,所以为了保证我们日后仍能顺利地查询到某个数的排名,所以我们要把它拆开放到合适的位置;
而此题要求将 \(x\) 序列放到 \(y\) 序列之后,我们直接 merge,虽然这样不保证以后分裂的时候左边权值和一定大于右边的权值和,但是注意到我判断分裂方式的语句是:
ll u=tree[tree[pos].l].siz+1;
if (u<=x)
即我是通过判断左子树大小来进行分裂的,与权值无关,所以我们可以以此来假装我们修改了权值来达到一样的效果。
- 要点:把 \(x\) 加到 \(y\) 后,是:\(merge(y,x)\);
完整代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x7fffffff
const ll maxn=300010;
ll tot,n,root,dl,dr,temp,m;
ll v[maxn];
struct node {ll siz,l,r,key,val,fa,sum;}tree[maxn<<1];
inline void pushup(ll pos) {
tree[pos].siz=tree[tree[pos].l].siz+tree[tree[pos].r].siz+1;
tree[pos].sum=tree[tree[pos].l].sum+tree[tree[pos].r].sum+tree[pos].key;
if (tree[pos].l) tree[tree[pos].l].fa=pos;
if (tree[pos].r) tree[tree[pos].r].fa=pos;
}
inline ll getrand(ll x) {
tree[++tot].siz=1;tree[tot].key=tree[tot].sum=x;tree[tot].val=rand();
tree[tot].fa=tot;
return tot;
}
inline void split(ll pos,ll x,ll &l,ll &r) {
if (!pos) {l=r=0;return;}
ll u=tree[tree[pos].l].siz+1;
if (u<=x) {
l=pos;
split(tree[l].r,x-u,tree[l].r,r);
}
else {
r=pos;
split(tree[r].l,x,l,tree[r].l);
}
tree[l].fa=l,tree[r].fa=r;
pushup(pos);
}
inline ll merge(ll l,ll r) {
if (!l||!r) return l|r;
if (tree[l].val<=tree[r].val) {
tree[l].r=merge(tree[l].r,r);
pushup(l);
return l;
}
else {
tree[r].l=merge(l,tree[r].l);
pushup(r);
return r;
}
}
inline ll getfa(ll pos) {
while (tree[pos].fa!=pos) pos=tree[pos].fa;
return pos;
}
inline ll getrank(ll pos) {
ll cnt=tree[tree[pos].l].siz+1;
while (tree[pos].fa!=pos) {
if (tree[tree[pos].fa].r==pos) cnt+=tree[tree[tree[pos].fa].l].siz+1;
pos=tree[pos].fa;
}
return cnt;
}
inline ll in() {
char a=getchar();
ll t=0,f=1;
while(a<'0'||a>'9') {if (a=='-') f=-1;a=getchar();}
while(a>='0'&&a<='9') {t=(t<<1)+(t<<3)+a-'0';a=getchar();}
return t*f;
}
signed main() {
n=in(),m=in();
for (ll i=1;i<=n;++i) {
v[i]=in();
getrand(v[i]);
}
for (ll i=1;i<=m;++i) {
char opt;
cin>>opt;
if (opt=='M') {
ll x=in(),y=in();
ll fx=getfa(x),fy=getfa(y);
if (fx==fy) continue;
merge(fy,fx);
}
else if (opt=='D') {
ll x=in();
ll fa=getfa(x);
split(fa,getrank(x)-1,dl,dr);
}
else {
ll x=in(),y=in();
ll fx=getfa(x),fy=getfa(y);
if (fx!=fy) {
puts("-1");
continue;
}
ll rx=getrank(x),ry=getrank(y);
if (rx>ry) swap(rx,ry);
split(fx,rx-1,dl,dr);
split(dr,ry-rx+1,temp,dr);
printf("%lld\n",tree[temp].sum);
merge(dl,merge(temp,dr));
}
}
return 0;
}

浙公网安备 33010602011771号