沉迷数据结构1(treap&非旋treap)

Achen大佬说不要沉迷数据结构否则智商会降低的。

从省选考完后就开始学treap,首先是自己yy了一个打了两百多行,然后debug了2个月还是3个月记不清了。

最后弃疗,去找了网上别人的代码抄了一遍。

noip考完后补常规的一段时间,羡慕Achen能20分钟打出一个treap模板,于是自己也开始走上打板子的不归路。

到了后来可以10分钟左右打出一个结构体版的treap,看了Achen的数组版treap,觉得自己结构体版的太不优秀啦,于是就换成数组版的。

然后现在有几周没有碰过treap,感觉又不会打了。。。。不过还好,大概看一下又想起来了。

先放个板子。

结构体版:

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
const int maxn=100000+10;
int n,tot=0,ans1,ans2,root;

int aa,ff;char cc;
int read() {
    aa=0;ff=1;cc=getchar();
    while((cc<‘0‘||cc>‘9‘)&&cc!=‘-‘) cc=getchar();
    if(cc==‘-‘) ff=-1,cc=getchar();
    while(cc>=‘0‘&&cc<=‘9‘) aa=aa*10+cc-‘0‘,cc=getchar();
    return aa*ff;
}

struct Node{
    int num,rnd,son[2],sum,x;
}node[maxn];

void rotate(int &pos,int p) {
    int s=node[pos].son[p];
    node[s].sum=node[pos].sum;
    node[pos].son[p]=node[s].son[!p];
    node[s].son[!p]=pos;
    node[pos].sum=node[pos].x+node[node[pos].son[0]].sum+node[node[pos].son[1]].sum;
    pos=s;
}

void add(int &pos,int x) {
    if(!pos) {
        pos=++tot;node[pos].num=x;node[pos].rnd=rand();
        node[pos].sum=node[pos].x=1;
        return ;
    }
    node[pos].sum++;
    if(node[pos].num==x) {
        node[pos].x++; return;
    }
    int p=x>node[pos].num;
    add(node[pos].son[p],x);
    if(node[node[pos].son[p]].rnd<node[pos].rnd) rotate(pos,p);
}

void del(int &pos,int x) {
    if(!pos) return;
    if(node[pos].num==x) {
        if(node[pos].x>1) {
            node[pos].x--;node[pos].sum--;return;
        }
        if(node[pos].son[0]*node[pos].son[1]==0) {
            pos=node[pos].son[0]+node[pos].son[1]; return;
        }
        int p= node[node[pos].son[1]].rnd<node[node[pos].son[0]].rnd;
        rotate(pos,p);
        node[pos].sum--;
        del(node[pos].son[!p],x);
    }
    else {
        node[pos].sum--;
        if(node[pos].num>x) del(node[pos].son[0],x);
        else del(node[pos].son[1],x);
    }
}

int qrank(int pos,int x) {
    if(node[pos].num==x) return node[node[pos].son[0]].sum+1;
    if(node[pos].num>x) return qrank(node[pos].son[0],x);
    return node[node[pos].son[0]].sum+node[pos].x+qrank(node[pos].son[1],x);
}

int qnum(int pos,int x) {
    if(x>node[node[pos].son[0]].sum&&x<=node[node[pos].son[0]].sum+node[pos].x) return node[pos].num;
    if(x<=node[node[pos].son[0]].sum) return qnum(node[pos].son[0],x);
    return qnum(node[pos].son[1],x-node[pos].x-node[node[pos].son[0]].sum);
}

void q1(int pos,int x) {
    if(!pos) return;
    if(x>node[pos].num) {
        ans1=node[pos].num;
        q1(node[pos].son[1],x);
    }
    else q1(node[pos].son[0],x);
}

void q2(int pos,int x) {
    if(!pos) return;
    if(x<node[pos].num) {
        ans2=node[pos].num;
        q2(node[pos].son[0],x);
    }
    else q2(node[pos].son[1],x);
}

int main() {
    srand((unsigned)time(NULL));
    n=read();
    int opt,x,y;
    for(int i=1;i<=n;++i) {
        opt=read();x=read();
        if(opt==1) add(root,x);
        else if(opt==2) del(root,x);
        else if(opt==3) printf("%d\n",qrank(root,x));
        else if(opt==4) printf("%d\n",qnum(root,x));
        else if(opt==5) q1(root,x),printf("%d\n",ans1);
        else q2(root,x),printf("%d\n",ans2);
    }
    return 0;
}

数组版:

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=1e5+10;
int n,tot,root,ans;
int sum[maxn],num[maxn],rnd[maxn],fx[maxn],son[maxn][2];

int aa,ff;char cc;
int read() {
    aa=0;cc=getchar();ff=1;
    while(cc<‘0‘||cc>‘9‘) {
        if(cc==‘-‘) ff=-1;
        cc=getchar();
    }
    while(cc>=‘0‘&&cc<=‘9‘) aa=aa*10+cc-‘0‘,cc=getchar();
    return aa*ff;
}

void rotate(int &pos,int p) {
    int s=son[pos][p];
    sum[s]=sum[pos];
    son[pos][p]=son[s][!p];
    son[s][!p]=pos;
    sum[pos]=sum[son[pos][0]]+sum[son[pos][1]]+fx[pos];
    pos=s;
}

void add(int &pos,int x) {
    if(!pos) {
        pos=++tot;
        num[pos]=x;
        rnd[pos]=rand();
    }
    sum[pos]++;
    if(num[pos]==x) {
        fx[pos]++;
        return;
    }
    int p=x>num[pos];
    add(son[pos][p],x);
    if(rnd[son[pos][p]]<rnd[pos]) rotate(pos,p);
}

void del(int &pos,int x) {
    if(!pos) return;
    if(num[pos]==x) {
        if(fx[pos]>1) {
            fx[pos]--;
            sum[pos]--;
            return;
        }
        if(son[pos][0]*son[pos][1]==0) {
            pos=son[pos][0]+son[pos][1];
            return;
        }
        int p=rnd[son[pos][1]]<rnd[son[pos][0]];
        rotate(pos,p);sum[pos]--;
        del(son[pos][!p],x);
    }
    else {
        sum[pos]--;
        del(son[pos][x>num[pos]],x);
    }
}

int qnum(int pos,int x) {
    if(x>sum[son[pos][0]]&&x<=sum[son[pos][0]]+fx[pos]) return num[pos];
    if(x<=sum[son[pos][0]]) return qnum(son[pos][0],x);
    return qnum(son[pos][1],x-sum[son[pos][0]]-fx[pos]);
}

int qrank(int pos,int x) {
    if(x==num[pos]) return sum[son[pos][0]]+1;
    if(x<num[pos]) return qrank(son[pos][0],x);
    return sum[son[pos][0]]+fx[pos]+qrank(son[pos][1],x);
}

void q1(int pos,int x) {
    if(!pos) return;
    if(num[pos]<x) ans=num[pos],q1(son[pos][1],x);
    else q1(son[pos][0],x);
}

void q2(int pos,int x) {
    if(!pos) return;
    if(num[pos]>x) ans=num[pos],q2(son[pos][0],x);
    else q2(son[pos][1],x);
}
int main() {
    srand(1031);
    n=read();
    int op,x;
    for(int i=1;i<=n;++i) {
        op=read();x=read();
        switch(op) {
            case 1:add(root,x);break;
            case 2:del(root,x);break;
            case 3:printf("%d\n",qrank(root,x));break;
            case 4:printf("%d\n",qnum(root,x));break;
            case 5:q1(root,x);printf("%d\n",ans);break;
            case 6:q2(root,x);printf("%d\n",ans);break;
        }
    }
    return 0;
}

而非旋treap支持split和merge很是有趣,这次直接照着Achen的板子学,感受到Achen大佬的数据结构多么优秀。

merge的时候不想用pair然后就乱搞了一搞,调了一阵子,最后强制把代码改成自己的码风。感觉不是很难打。

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
#define ll long long
#define lc son[pos][0]
#define rc son[pos][1]
const int maxn=1e5+10,base=28;
int n,root,tot,ans;
int son[maxn][2],num[maxn],sum[maxn],rnd[maxn];

int aa,ff;char cc;
int read() {
	aa=0;ff=1;cc=getchar();
	while(cc!=‘-‘&&(cc<‘0‘||cc>‘9‘)) cc=getchar();
	if(cc==‘-‘) ff=-1,cc=getchar();
	while(cc>=‘0‘&&cc<=‘9‘) aa=aa*10+cc-‘0‘,cc=getchar();
	return aa*ff;
}

void ud(int pos){sum[pos]=sum[lc]+sum[rc]+1;}
ll pr(int x,int y) {return ((ll)x<<base)+(ll)y;}//第一个ll要套在里面
int fi(ll x){return x>>base;}
int se(ll x){return x-((ll)fi(x)<<base);}

int merge(int x,int y) {
	if((ll)x*y==0) return x^y;
	if(rnd[x]<rnd[y]) return son[x][1]=merge(son[x][1],y),ud(x),x;
	else return son[y][0]=merge(x,son[y][0]),ud(y),y;
}

ll split(int pos,int x) {
	if(!pos) return 0;
	ll rs;
	if(sum[lc]>=x) {
		rs=split(lc,x);
		lc=se(rs);
		rs=pr(fi(rs),pos);
	}
	else {
		rs=split(rc,x-sum[lc]-1);
		rc=fi(rs);
		rs=pr(pos,se(rs));
	}
	return ud(pos),rs;
}

int qrank(int pos,int x) {
	if(!pos) return 1;
	if(num[pos]<x) return sum[lc]+1+qrank(rc,x);
	return qrank(lc,x);
}

int qnum(int pos,int x) {
	if(x==sum[lc]+1) return num[pos];
	if(x<=sum[lc]) return qnum(lc,x);
	return qnum(rc,x-sum[lc]-1);
}

void q1(int pos,int x) {
    if(!pos) return;
    if(num[pos]<x) ans=num[pos],q1(son[pos][1],x);
    else q1(son[pos][0],x);
}

void q2(int pos,int x) {
    if(!pos) return;
    if(num[pos]>x) ans=num[pos],q2(son[pos][0],x);
    else q2(son[pos][1],x);
}

void add(int &pos,int x) {
	int k=qrank(pos,x); ++tot;
	num[tot]=x;sum[tot]=1;rnd[tot]=rand();
	ll p=split(pos,k-1);
	pos=merge(fi(p),tot);
	pos=merge(pos,se(p));
}

void del(int &pos,int x) {
	int k=qrank(pos,x);
	ll p=split(pos,k-1);
	ll q=split(se(p),1);
	pos=merge(fi(p),se(q));
}

int main() {
	srand(1031);n=read();
	int op,x;
	while(n--) {
		op=read(); x=read();
		switch(op) {
			case 1:add(root,x);break;
			case 2:del(root,x);break;
			case 3:printf("%d\n",qrank(root,x));break;
			case 4:printf("%d\n",qnum(root,x));break;
			case 5:q1(root,x);printf("%d\n",ans);break;
            case 6:q2(root,x);printf("%d\n",ans);break;
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/Serene-shixinyi/p/8110765.html

时间: 2024-10-10 04:25:15

沉迷数据结构1(treap&非旋treap)的相关文章

2827: 千山鸟飞绝 非旋treap

国际惯例的题面:看起来很不可做的样子,我们先来整理一下题意吧.就是,维护每个点曾经拥有过的最大的两个属性值,支持把点的位置移动.我们用map对每个位置进行离散化,对每个位置建立一个平衡树.为了方便分离,这个平衡树的关键字是节点编号.然后把每个点当做一个节点,放进其所在位置的平衡树里.剩下要做的就是平衡树分离出一个点,合并一个点,还有打标记了.对于士气值的标记,我们维护平衡树中的max,每次合并的时候,用这个新点的威武值去给整棵树打标记,再用树中的max给这个新点打标记.团结值的标记,合并后一起打

非旋 treap 结构体数组版(无指针)详解,有图有真相

非旋  $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ 不依靠旋转,只有两个核心操作merge(合并)和split(拆分) 所谓随机数维护平衡就是给每个节点一个随机值 key (下文中没有加随机的就代表是真实权值), 然后整棵树中 key 值要满足小(大)根堆的性质(也就是heap), 同时也要满足平衡树(tree)的性质(也就是每个节点左子树内节点真实

非旋Treap总结 : 快过Splay 好用过传统Treap

非旋$Treap$ 其高级名字叫$Fhq\ Treap$,既然叫$Treap$,它一定满足了$Treap$的性质(虽然可能来看这篇的人一定知道$Treap$,但我还是多说几句:$Fhp\ Treap$就是继承了$Treap$的随机系统,在二叉搜索的基础上,每个点加一个随机化处理,这些随机值满足堆的性质……通俗一点讲,就是$Fhp\ Treap$它每个点有至少两个值,一个是val,即存的数值,这些数值满足二叉搜索树,也就是父亲比左孩子小/大,则右孩子比父亲小/大:还有一个是key,是个随机值,这些

平衡树讲解(旋转treap,非旋转treap,splay)

在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用的:treap和splay(其中treap包括旋转treap和非旋转treap). 一.treap treap这个词是由tree和heap组合而成,意思是树上的的堆(其实就是字面意思啦qwq).treap可以说是由二叉搜索树(BST)进化而来,二叉搜索树每个点满足它左子树中所有点权值都比它小,它右子

非旋Treap

最近看到有一种不用旋转的treap,好像还可以持久化,于是就学了一下. 如果你还不会Treap的话,那你可以点击这里,对旋转Treap有个大致了解,这里就不赘述Treap的性质了. treap就是tree+heap.它的每个节点的权值data满足排序二叉树的性质,随机权值key满足堆的性质.由于key是随机的所以它大致是平衡的. 不基于旋转的treap有两个基本操作: merge(a,b):返回一个treap,包含a,b两个treap中的所有节点,但要保证b中所有节点权值都大于等于a. spli

[BZOJ 3224] 普通平衡树 非旋Treap

题意 维护一个多重集合 $S$ , 支持: ① 插入一个数 $w$ . ② 删除一个数 $w$ . ③ 查询 $w$ 在集合中的排名. ④ 查询集合中排名第 $r$ 的数. ⑤ 求集合中 $w$ 的前驱. ⑥ 求集合中 $w$ 的后继. $N \le 100000$ . 小结 要总结一些常见的写法和想法, 减小实现时的复杂度. 实现 #include <cstdio> #include <cstring> #include <cstdlib> #include <

模板 - 数据结构 - 无旋Treap / FHQ Treap

普通平衡树: #include<bits/stdc++.h> using namespace std; typedef long long ll; #define ls(p) ch[p][0] #define rs(p) ch[p][1] const int MAXN = 100000 + 5; int val[MAXN], ch[MAXN][2], rnd[MAXN], siz[MAXN], tot, root; void Init() { tot = root = 0; } void Pu

[BZOJ3223]文艺平衡树 无旋Treap

3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 Input 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2--n-1,n)  m表示翻转操作次数接下来m行每行两个数[l,r] 数据保证 1<=l<

[您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我们还是用rand()来实现平衡 而无旋treap与treap不同的地方,也是其核心,就是它不旋转用两个新的核心函数:merge函数(合并两棵子树)和split函数(分裂出某棵树的前k个节点,并且作为一棵树返回) 首先看merge函数,它是一个递归实现的过程,先看代码: 1 Treap *merge(