luogu 【P3377】 【模板】左偏树

左偏树模板。。。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <iostream>
#define For(i, l, r) for(int i = (l); i <= (int)(r); ++i)
#define Fordown(i, r, l) for(int i = (r); i >= (int)(l); --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

inline int read(){
  int x = 0, fh = 1; char ch;
  for(; !isdigit(ch); ch = getchar()) if (ch == ‘-‘) fh = -1;
  for(; isdigit(ch); ch = getchar()) x = (x<<1) + (x<<3) + (ch^‘0‘);
  return x * fh;
}

struct node {
	int lc, rc, val;  //分别是left_child左儿子, right_child右儿子, value 键值
};
const int max_node = 100100; //最大子节点个数
node lt[max_node]; //leftist_tree 左偏树
int dist[max_node], fa[max_node]; //左偏树距离dist:到最右叶子节点边的条数
//并查集父亲数组fa

void make_tree (int x) {
	lt[x].lc = lt[x].rc = dist[x] = 0; //清空它的左右儿子和距离(新建一个树)
}

int merge (int a, int b) {
	if (a == 0) return b; //a为空 返回b
	if (b == 0) return a; //b为空 返回a
	if (lt[a].val > lt[b].val) swap(a, b); //将根节点设为a(值较小的那个)
	lt[a].rc = merge(lt[a].rc, b); fa[lt[a].rc] = a; //将b合并到a的右子树上 并将右子树父亲设为a
	if (dist[lt[a].rc] > dist[lt[a].lc]) swap(lt[a].lc, lt[a].rc); //右子节点距离大于左子节点 不符合左偏树性质则交换左右子树
	if (lt[a].rc == 0) dist[a] = 0; //右子树为空 距离为0 即直接到自己
	else dist[a] = dist[lt[a].rc] + 1; //否则按照性质 距离为右子节点的距离+1
	return a; //返回a节点
}

int find (int x) {
	return fa[x] = fa[x] == x ? x : find(fa[x]); //并查集操作
}

int main(){
	int n = read(), m = read();
	For (i, 1, n) {
		lt[i].val = read();
		make_tree(i);
		fa[i] = i;
	}
	while (m--) {
		int opt = read();
		if (opt == 1) {
			int a = read(), b = read();
			int root_a = find(a), root_b = find(b);
			if (root_a == root_b) continue; //在同一个堆里就不合并
			if (lt[a].val == 0 || lt[b].val == 0) continue; //这个数已被删掉也不合并
		//	cout << "merge:  " << root_a << ‘ ‘ << root_b << endl;
			int tmp = merge(root_a, root_b);
			fa[root_a] = fa[root_b] = tmp; //将两个节点父亲设为合并后的父亲
		}
		else {
			int a = read();
			int root_a = find(a);
			if (lt[a].val == 0) {printf ("-1\n"); continue;} //已被删除 输出-1
		//	cout << "find: " << root_a << endl;
			printf ("%d\n", lt[root_a].val);
			int tmp = merge (lt[root_a].lc, lt[root_a].rc);
			fa[lt[root_a].lc] = fa[lt[root_a].rc] =  tmp; //将左右子树的父亲设为它们合并后的父亲
			lt[root_a].lc = lt[root_a].rc = lt[root_a].val = 0; //删除堆顶
		}
	}
}
时间: 2024-08-24 04:11:00

luogu 【P3377】 【模板】左偏树的相关文章

模板 - 左偏树 + 并查集

这两个经常混在一起用的样子,封成同一个好了. #include<bits/stdc++.h> using namespace std; typedef long long ll; int solve(); int main() { #ifdef Yinku freopen("Yinku.in","r",stdin); #endif // Yinku solve(); } int n,m; const int MAXN=100005; int tot,v[

算法模板——左偏树(可并堆)

实现的功能——输入1 x,将x加入小根堆中:输入2,输出最小值并去在堆中除掉 1 var 2 i,j,k,l,m,n,head:longint; 3 a,lef,rig,fix:array[0..100000] of longint; 4 function min(x,y:longint):longint;inline; 5 begin 6 if x<y then min:=x else min:=y; 7 end; 8 function max(x,y:longint):longint;inl

[模板]左偏树

可并堆 可以支持合并的堆. /*大根堆*/ struct heap{ int l,r,w; }h[N]; int rt[N];//第i个堆的根的下标 /*合并以x,y为根的堆*/ inline int merge(int x,int y){ int t; //其中一个堆为空 if(!x||!y) return x+y; //使得x,y两个根中x大 if(h[x].w<h[y].w){ int t=x;x=y;y=t; } //保持堆两边的平衡 h[x].r=merge(y,h[x].r); t=

模板:左偏树

如果你知道priority_queue的话,那自然就知道左偏树的目的了. 左偏树的目的和优先队列一致,就是求出当前所在堆中的最大(小)值. 但是我们作为高贵的C++选手,我们为什么还要学习左偏树呢. 当然是因为priority_queue太!慢!了! ———————————————————————————————————— 概念引入: 对于左偏树,我们引入两个概念: 外节点:如果该节点的左子树或右子树为空,那么该节点为外节点. 距离(dis):该节点到达最近的外节点经过的边的个数. 我们同时将优

Luogu P3377【模板】左偏树(可并堆)

采用了jls的左偏树写法,非常好写... 额外用一个并查集维护集合关系即可,注意路径压缩. #include <bits/stdc++.h> using namespace std; typedef long long LL; #define LOG(...) fprintf (stderr, __VA_ARGS__) #define pb push_back #define mp make_pair #define SZ(x) ((int)(x).size()) #define ALL(x)

P3377 【模板】左偏树(可并堆)

P3377 [模板]左偏树(可并堆) 题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作) 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作) 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示一开始小根堆的个数和接下来操作的个数. 第二行包含N个正整数,其中第i个

P3377 【模板】左偏树(可并堆) 左偏树浅谈

因为也是昨天刚接触左偏树,从头理解,如有不慎之处,跪请指教. 左偏树: 什 么是(fzy说)左偏树啊? 前置知识: 左偏树中dist:表示到右叶点(就是一直往右下找,最后一个)的距离,特别的,无右节点的为0. 堆:左偏树是个堆. 关于左偏性质:可以帮助堆合并(研究深了我也不懂的,看代码理解) 对于任意的节点,dist[leftson]>=dist[rightson],体现了左偏性质. 同理可得:对于任意右儿子的父亲节点的dist自然等于右儿子的dist+1喽 关于各种操作: merge: 是插入

【模板】左偏树

左偏树是可合并堆的一种实现方式,可合并堆还有其他实现方式比如斜堆,然而我这种蒟蒻只会写左偏树. 模板里的左偏树为大根堆,支持合并,查询堆顶和弹出堆顶操作,对于已经删除的位置,查询将返回-1,为了确保弹出的正常进行,模板里使用的并查集没有使用路径压缩,因此常数可能会比较大. 1 #include<stdio.h> 2 #define maxn 1000 3 struct node{int ch[2],w,dist;}; 4 int n,op,ori[maxn]; 5 void swp(int &

左偏树(p3377)

题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作) 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作) 输入格式 第一行包含两个正整数N.M,分别表示一开始小根堆的个数和接下来操作的个数. 第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数. 接下来