题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1455
题解:
本题显然可以用堆来实现,(维护一个大根堆),但是无法进行合并操作,于是我们想到左偏树。
定义一个结点的斜深度为这个节点不断向自己的右儿子走 直到为叶子节点的长度。左偏树的“左偏”指左儿子的斜深度一定大于等于右儿子的斜深度。合并就简单了,我们可以归并的来维护一个左偏树,设需合并的两个树的根节点为 k1 k2,那么我们可以先将以 k1 为根节点的右子树和 k2 那个子树合并,再不断调整即可。
PS:数组一定要开大,重要的事情说一遍!我因此RE 4次!
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map> // STL
#include <string>
#include <vector>
#include <queue>
#include <stack>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
using namespace std;
typedef long long LL;
const int inf = 1 << 26;
struct trees {
int l, r, w, size;
} t[1000005];
int n, Q;
int fa[1000005], die[1000005];
int find(int x) {
if(fa[x] == x) return fa[x];
else return fa[x] = find(fa[x]);
}
int merge(int k1, int k2) { // 合并以 k1 k2 为根节点的左偏树,返回合并后树的根
if(!k1 || !k2) return k1+k2;
if(t[k1].w > t[k2].w) swap(k1, k2);
t[k1].r = merge(t[k1].r, k2);
if(t[t[k1].r].size > t[t[k1].l].size) swap(t[k1].r, t[k1].l);
t[k1].size = t[t[k1].l].size + t[t[k1].r].size + 1;
return k1;
}
int main(){
scanf("%d", &n);
for ( int i = 1; i <= n; i ++ ) scanf("%d", &t[i].w);
for ( int i = 1; i <= n; i ++ ) fa[i] = i;
scanf("%d", &Q);
while( Q -- ) {
char ch[25];
scanf("%s", ch);
if(ch[0] == 'M') {
int x, y;
scanf("%d %d", &x, &y);
if(die[x] || die[y]) continue;
int fx = find(x), fy = find(y);
if(fx != fy) {
int temp = merge(fx, fy);
fa[fx] = fa[fy] = temp;
}
} else if(ch[0] == 'K') {
int x;
scanf("%d", &x);
if(die[x]) { puts("0"); continue; }
int temp = find(x); die[temp] = 1;
printf("%d\n", t[temp].w);
fa[temp] = merge(t[temp].l, t[temp].r);
fa[fa[temp]] = fa[temp];
}
}
return 0;
}