银河英雄传说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 有这两种形式:

  1. 第一种:
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);
}
  1. 第二种:
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;
}
posted @ 2023-07-10 02:30  Pwtking  阅读(21)  评论(0)    收藏  举报