• treap树【一本通题目】


    Treap树:平衡树的一种

    数和堆的集合,每个节点有值,也有优先级,那么这棵树的形态就被确定了,和插入顺序无关了(有赖于优先级

    避免退化成链:在生成节点时,随机生成优先级,然后插入时动态调整

    删除:如果有两个子节点,找到优先级大的,把x向反方向旋转,也就是把x向树的下层调整,直到旋转到叶子节点

    !很多题目涉及名次树

    常用操作:

    struct node,旋转rotate,插入insert(),查找第k大的数kth(),查询某个数find()【名次树】

    旋转zig(右旋)/tag(左旋)操作

    splay树和treap树https://www.cnblogs.com/shirlybaby/p/12393268.html 

    【一本通】

    1565:【例 1】营业额统计

     有很多种方法

    第一种方法;直接用set做

    但是注意一点(怎么看出来的...)就是有一个点差一个输入,其他直接lowerboundupperbound就可以了

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //可以用set过
    //有一个点少了一个输入的样子 
    set<int> st;
    int n,x; 
    int main(){
    	scanf("%d",&n);
    	int ans;
    	scanf("%d",&x);
    	ans=x;
    	st.insert(INF);
    	st.insert(-INF);
    	//插入正无穷和负无穷,防止迭代器访问到一些奇奇怪怪的内存
    	st.insert(x);  //第一个单独考虑
    	for(int i=1;i<n-1;i++){
    		scanf("%d",&x);
    		int l=*(--st.lower_bound(x)); //大于等于(第一个)所以减一就是小于 
    		int r=*st.lower_bound(x);  //大于 
    		if(x-l>r-x) ans+=r-x;
    		else ans+=x-l;
    		st.insert(x);
    	}
    	if(scanf("%d",&x)==1){
    		int l=*(--st.lower_bound(x));
    		int r=*st.lower_bound(x);
    		if(x-l>r-x) ans+=r-x;
    		else ans+=x-l;
    		st.insert(x);
    	}
    	else{
    		x=0;
    		int l=*(--st.lower_bound(x));
    		int r=*st.lower_bound(x);
    		if(x-l>r-x) ans+=r-x;
    		else ans+=x-l;
    		st.insert(x);
    	}
    	printf("%d
    ",ans); 
    return 0;
    }
    

    第二种方法:离线+排序

    用链表做,不容易想到

    首先排序,然后插入链表
    r[i]表示原来的a[i]在链表中的位置
    倒序处理,把r[i]的ans贡献算出来,然后删除r[i]指向的位置
    为什么这样可行呢?
    因为是离线+排序,所以每个元素之间的关系是已知的
    倒序处理时,对于每个a[i],链表就是前i个元素组成的【学会呀!】

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    /*
    首先排序,然后插入链表
    r[i]表示原来的a[i]在链表中的位置
    倒序处理,把r[i]的ans贡献算出来,然后删除r[i]指向的位置
    为什么这样可行呢?
    因为是离线+排序,所以每个元素之间的关系是已知的
    倒序处理时,对于每个a[i],链表就是前i个元素组成的
    */
    typedef unsigned long long ull;
    struct node{
    	int pre,next;
    }b[32768];
    struct data{
    	int d;
    	int rak;
    }a[32768];
    int r[32768];
    int n,ans;
    bool cmp(data x,data y){
    	if(x.d!=y.d) return x.d<y.d;
    	else return x.rak<y.rak;
    }
    void del(int x){
    	b[b[x].next].pre=b[x].pre;
    	b[b[x].pre].next=b[x].next;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i].d);
    		a[i].rak=i;
    	}
    	sort(a+1,a+1+n,cmp);
    	for(int i=1;i<=n;i++){
    		r[a[i].rak]=i; //r[]表示的是原来的下标在现在的排名 
    		if(i!=n) b[i].next=i+1;
    		b[i].pre=i-1;
    	}
    	int j,k;
    	for(int i=n;i;i--){
    		j=0;k=0;
    		int x=r[i];  //原来的输入里面第i个现在的排名 
    		if(b[x].pre) j=abs(a[b[x].pre].d-a[x].d);
    		else j=INF;
    		if(b[x].next) k=abs(a[b[x].next].d-a[x].d);
    		else k=INF;
    		if(i!=1) ans+=min(j,k);
    		else ans+=a[x].d;
    		del(x);
    		r[i]=0; 
    	}
    	printf("%d
    ",ans);
    	
    return 0;
    }
    

    第三种方法:splay树

    提根操作!左右旋操作,这个不好写也不好记

    注意如果发现已经有这个数的话,就不要再计算前驱和后继了,肯定加的是0

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=100100;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //第三种做法   平衡树 splay
    int root,tot;
    struct tree{
    	int key;
    	int son[2];  //son[0]左儿子,son[1]右儿子
    	int fa;   
    }T[maxn]; 
    void newnode(int &r,int w,int fa){
    	r=++tot;
    	T[r].key=w;
    	T[r].fa=fa;
    	T[r].son[0]=T[r].son[1]=0;
    }
    void rotat(int r,int kind){  //旋转,kind为1是右旋,为0是左旋
    	int y=T[r].fa;
    	T[y].son[!kind]=T[r].son[kind];
    	T[T[r].son[kind]].fa=y;
    	if(T[y].fa)
    	T[T[y].fa].son[T[T[y].fa].son[1]==y]=r;
    	T[r].fa=T[y].fa;
    	T[r].son[kind]=y;
    	T[y].fa=r;
    }
    void splay(int r,int goal){  //Splay调整,将结点r调整到goal下面(以r为根节点)
    	int y;
    	while(T[r].fa!=goal){
    		y=T[r].fa;
    		if(T[y].fa==goal) rotat(r,T[y].son[0]==r); //如果是左子树就右旋 右子树就左旋 
    		//如果现在的爸爸的爸爸就是目标的话,就旋转 
    		else{
    			int kind=T[T[y].fa].son[0]==y;
    			if(T[y].son[kind]==r){
    				rotat(r,!kind);
    				rotat(r,kind);
    			}
    			else{
    				rotat(y,kind);
    				rotat(r,kind);
    			}
    		}
    	}
    	if(goal==0) root=r; //更新根节点
    } 
    bool inser(int w){
    	int r=root;
    	while(T[r].son[w>T[r].key]){
    		if(T[r].key==w){   //如果w已经在树中
    			splay(r,0);   //移到树根
    			return false;  //返回0,不用继续插入了 
    		}
    		r=T[r].son[w>T[r].key]; //如果大的话,就插入右子树(1),小的话就插入左子树(0) 
    	}
    	newnode(T[r].son[w>T[r].key],w,r);  //插在r相应的子树里面   void newnode(int &r,int w,int fa)
    	splay(T[r].son[w>T[r].key],0);
    	//移动到树根
    	return true; 
    }
    int get_pre(int r){  //在左子树中找到最大的值(找前驱)
    	int temp=T[r].son[0];
    	if(temp==0) return INF;
    	while(T[temp].son[1])   //在左子树的右子树里面寻找 
    		temp=T[temp].son[1];
    	return T[r].key-T[temp].key;
    }
    int get_next(int r){  //在右子树中找到最小的值(找后继)
    	int temp=T[r].son[1];
    	if(temp==0) return INF;
    	while(T[temp].son[0])
    	temp=T[temp].son[0];
    	return T[temp].key-T[r].key;
    }
    int main(){
    	int n,x;
    	scanf("%d",&n);
    	int ans=0;
    	scanf("%d",&x);
    	ans=x;
    	newnode(root,x,0);
    	for(int i=1;i<n;i++){
    		scanf("%d",&x);
    		if(!inser(x)) continue;
    		ans+=min(get_pre(root),get_next(root));
    	}
    	printf("%d
    ",ans);
    return 0;
    }
    

    第四种方法:treap树

    每次取出新读入数据的前驱与后继,与该数据差值较小的就是。(注意若已有同一个数据,特判ans+=0)

    #include<cstdio>
    #include<iostream>
    #include<cstdlib>
    #include<cmath>
    using namespace std;
    const int maxn = 100010;
    inline void qread(int &x) {
        x = 0;
        char ch = getchar();
    	int flag = 0;
        while(ch <'0' || ch >'9')        {
            if(ch =='-')    flag = 1;
            ch = getchar();
        }
        while(ch >='0'&& ch <='9')    x = 10 * x + ch - 48, ch = getchar();
        if(flag)    x = -x;
    }
    int rt = 0, tpn, v[maxn], l[maxn], r[maxn], R[maxn], sz[maxn], vn[maxn];
    inline void update(int x) {
        sz[x] = sz[l[x]] + sz[r[x]] + vn[x];
    }
    inline void zig(int &x) {
        int t = l[x];
        l[x] = r[t];
        r[t] = x;
        update(x);
        update(t);
        x = t;
    }
    inline void zag(int &x) {
        int t = r[x];
        r[x] = l[t];
        l[t] = x;
        update(x);
        update(t);
        x = t;
    }
    void insert(int &x, int w) {
        if(!x)    {
            x = ++tpn;
            v[x] = w;
            R[x] = rand();
            sz[x] = vn[x] = 1;
            return ;
        }
        if(w < v[x])    insert(l[x], w);
        else if(w > v[x])    insert(r[x], w);
        else            vn[x]++;
        update(x);
        if(l[x] && R[l[x]] < R[x])    zig(x);
        if(r[x] && R[r[x]] < R[x])    zag(x);
    }
    void pop(int &x, int w) {
        if(w < v[x])    pop(l[x], w);
        else if(w > v[x])    pop(r[x], w);
        else {
            if(vn[x] > 1)    vn[x]--;
            else if(!l[x] || !r[x])    x = l[x] | r[x];
            else if(R[l[x]] < R[r[x]])    zig(x), pop(x, w);
            else                        zag(x), pop(x, w);
        }
        update(x);
    }
    int ask(int x, int w){
        if(!x)        return -1;
        if(w < v[x])    return ask(l[x], w);
        if(w > v[x])    return ask(r[x], w);
        return     x;
    }
    int findk(int x, int k){
        if(k >= sz[l[x]] + 1 && k <= sz[l[x]] + vn[x])    return v[x];
        if(k <= sz[l[x]])    return findk(l[x], k);
        return     findk(r[x], k - sz[x] - vn[x]);
    }
    int findpre(int x, int w){
        int res = -0x3f3f3f3f;
        while(x){
            if(w <= v[x])    x = l[x];
            else            res = v[x], x = r[x];
        }
        return res;
    }
    int findnxt(int x, int w){
        int res = 0x3f3f3f3f;
        while(x){
            if(w < v[x])    res = v[x], x = l[x];
            else            x = r[x];
        }
        return res;
    }
    int main(void) {
        srand(11535);
        int n, ans;
        qread(n);
        qread(ans);
        insert(rt, ans);
        for(int i=2; i<=n; ++i){
            int x;
            qread(x);
            int l = findpre(rt, x), r = findnxt(rt, x);    
            if(ask(rt, x) != -1)    {insert(rt, x); continue;}
            insert(rt, x);
            ans += min(x - l, r - x);
        }    
        printf("%d
    ", ans);
    }
    

      还有第五种方法是线段树、第六种替罪羊树(不知道没写了)

    1566:宠物收养所

    这道题和前面一道很相似,都是需要计算相差最小的前驱和后继,适合使用treap或者splay这样的数据结构

    这个跟 营业额统计 那题差不多,多了个标记。标记一下当前这个树里的是人还是狗。判断一下当前操作的与树里的是不是同种生物,然后相应做出处理就好了。
    首先是treap的做法

    #include<bits/stdc++.h>
    using namespace std;
    const int N=80005;
    const int mod=1000000;
    const int inf=1e9;
    int n,ans,a,b,R,sum,num[N];
    int ch[N][2],size[N],val[N],rd[N];
    void rotate(int &q,int d)
    {
        int k=ch[q][d];
        ch[q][d]=ch[k][d^1];
        ch[k][d^1]=q;
        size[k]=size[q];
        size[q]=size[ch[q][0]]+size[ch[q][1]]+1;
        q=k;
    }
    void ins(int &q,int x)
    {
        if(!q)
        {
            q=++sum;
            size[q]=1;val[q]=x;
            rd[q]=rand(); return;
        }
        size[q]++;
        if(val[q]<x)
        {
            ins(ch[q][1],x);
            if(rd[q]>rd[ch[q][1]]) rotate(q,1);
        }
        else
        {
            ins(ch[q][0],x);
            if(rd[q]>rd[ch[q][0]]) rotate(q,0);
        }
    }
    void del(int &q,int x)
    {
        if(!q) return;
        if(val[q]==x)
        {
            if(ch[q][0]==0||ch[q][1]==0) q=ch[q][0]+ch[q][1];
            else if(rd[ch[q][0]]<rd[ch[q][1]]) rotate(q,0),del(q,x);
            else rotate(q,1),del(q,x);
        }
        else
        {
            size[q]--;
            if(val[q]>x) del(ch[q][0],x);
            else del(ch[q][1],x);
        }
    }
    int qq(int q,int x)
    {
        if(!q) return -inf;
        if(val[q]>x) return qq(ch[q][0],x);
        else return max(val[q],qq(ch[q][1],x));
    }
    int hq(int q,int x)
    {
        if(!q) return inf;
        if(val[q]<x) return hq(ch[q][1],x);
        else return min(val[q],hq(ch[q][0],x));
    }
    int main()
    {
        scanf("%d",&n);int c=-1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a,&b);
            if(c==a) ins(R,b);
            else if(size[R]){
                int hhh=qq(R,b),ggg=hq(R,b);
                if(b-hhh<=ggg-b) ans=(ans+b-hhh)%mod,del(R,hhh);
                else ans=(ans+ggg-b)%mod,del(R,ggg);
            }
            else c=a,ins(R,b);
        }
        printf("%d",ans);
        return 0;
    } 
    

    第二种方法:splay树

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<vector>
    #include<algorithm>
    using namespace std;
    #define MAX 200000
    #define MOD 1000000
    int tot;
    inline int read()
    {
    	register int x=0,t=1;
    	register char ch=getchar();
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-'){t=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    	return x*t;
    }
    struct Node
    {
    	int ch[2];
    	int val;
    	int ff;
    }t[MAX];
    int root;
    inline void rotate(int x)
    {
    	int y=t[x].ff;
    	int z=t[y].ff;
    	int k=(x==t[y].ch[1]);
    	t[z].ch[y==t[z].ch[1]]=x;
    	t[x].ff=z;
    	t[y].ch[k]=t[x].ch[k^1];
    	t[t[x].ch[k^1]].ff=y;
    	t[x].ch[k^1]=y;
    	t[y].ff=x;
    }
    inline void splay(int x,int goal)
    {
    	//if(x==0)return;
    	while(t[x].ff!=goal)
    	{
    		int y=t[x].ff;
    		int z=t[y].ff;
    		if(z!=goal)
    			(t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y);
    		rotate(x);
    	}
    	if(goal==0)root=x;
    }
    inline void insert(int x)
    {
    	int u=root,ff=0;
    	while(u&&t[u].val!=x)
    	{
    		ff=u;
    		u=t[u].ch[t[u].val<x];
    	}
    	if(u);
    	else
    	{
    		u=++tot;
    		if(ff)t[ff].ch[t[ff].val<x]=u;
    		t[u].ff=ff;
    		t[u].ch[0]=t[u].ch[1]=0;
    		t[u].val=x;
    	}
    	splay(u,0);
    }
    inline void find(int x)
    {
    	int u=root;
    	if(!u)return;
    	while(t[u].ch[x>t[u].val]&&x!=t[u].val)
    		u=t[u].ch[x>t[u].val];
    	splay(u,0);
    }
    inline int Next(int x,int f)
    {
    	find(x);
    	int u=root;
    	if(t[u].val>=x&&f)return u;
    	if(t[u].val<=x&&!f)return u;
    	u=t[u].ch[f];
    	while(t[u].ch[f^1])u=t[u].ch[f^1];
    	return u;
    }
    inline int Next_une(int x,int f)
    {
    	find(x);
    	int u=root;
    	if(t[u].val>x&&f)return u;
    	if(t[u].val<x&&!f)return u;
    	u=t[u].ch[f];
    	while(t[u].ch[f^1])u=t[u].ch[f^1];
    	return u;
    }
    inline void Delete(int x)
    {
    	int lt=Next_une(x,0);
    	int nt=Next_une(x,1);
    	splay(lt,0);splay(nt,lt);
    	t[nt].ch[0]=0;
    }
    int main()
    {
    	int n=read();
    	int cnt=0,ans=0;
    	insert(+214748364);
    	insert(-214748364);
    	while(n--)
    	{
    		int k=read(),x=read();
    		if(x==1)
    		    x=1;
    		if(cnt==0)//空树
    			insert(x);
    		if(cnt>0)//宠物树
    		{
    			if(k==0)insert(x);
    			else//新来顾客
    			{
    				int a1=t[Next(x,0)].val;//前驱
    				int a2=t[Next(x,1)].val;//后继
    				if(abs(a1-x)<=abs(a2-x))
    				{
    					ans+=abs(a1-x);
    					Delete(a1);
    				}
    				else
    				{
    					(ans+=abs(a2-x))%=MOD;
    					Delete(a2);
    				}
    			}
    		}
    		if(cnt<0)//顾客树
    		{
    			if(k==1)insert(x);
    			else//新来宠物
    			{
    				int a1=t[Next(x,0)].val;
    				int a2=t[Next(x,1)].val;
    				if(abs(a1-x)<=abs(a2-x))
    				{
    					(ans+=abs(a1-x))%=MOD;
    					Delete(a1);
    				}
    				else
    				{
    					(ans+=abs(a2-x))%=MOD;
    					Delete(a2);
    				}
    			}
    		}
    		cnt=cnt+(k==0?1:-1);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

      

    1567:郁闷的出纳员

     其实看题目就能够明白:需要做的事有增加或减少每一个节点的值,增加节点、删除节点,查询排名为第k的人的值

    节点的值是会动态改变,还可能动态删除的,而重点是这道题想问当前工资第k多的员工所拿的工资数
    先写一个最简单的vector https://www.sogou.com/link?url=hedJjaC291P3yGwc7N55kLSc2ls_Ks2xYJXKjZDwcU0p1wk5xGC9fwNZoNd2gwAGIeZ8aQ291kY.
    /*
    vector通过lower_bound和insert操作能够维护一个有序递减/递增的线性表
    insert复杂度虽然是O(n),实际表现却非常快(可以看做sqrt(n)),配合lower_bound能够做到许多事情
    在这道题中,我们重载了lower_bound的<运算符,维护一个单调递减的序列
    ps
    erase的复杂度为O(n),因为是区间删除,我们要避免用erase操作,注意到删除的区间是序列中最小的一段区间,考虑用O(1)的pop_back
    pop_back的复杂度是O(1),元素总数不超过100000,故删除操作总复杂度为O(N)
    查询元素总数时,我们也不用STL的size()(单次复杂度O(N)),而是手动维护一个siz变量

     但是我对delta偏移的用法还不是很懂

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e6+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int n,m;
    int k;
    vector<int> q;
    bool cmp(int a,int b) {
    	return a>b;
    }
    int delta=0; //用这个记录偏移 就是增加或者减小的工资 
    int main(){
    	scanf("%d %d",&n,&m);
    	char op;
    	int size=0,ans=0;  //手动维护集合大小 
    	for(int i=0;i<n;i++){
    		scanf(" %c",&op);
    		scanf("%d",&k);
    		switch(op){
    			case 'I':{
    				if(k>=m){
    					q.insert(lower_bound(q.begin(),q.end(),k-delta,cmp),k-delta);  //为什么插入的是k-delta 
    					size++;
    				}
    				break;
    			}
    			case 'A':
    			 delta+=k;break;
    			case 'S':{
    				delta-=k;
    				int it=upper_bound(q.begin(),q.end(),m-delta,cmp)-q.begin();   //为什么比较的是m-delta 
    				while(size!=it) {
    					q.pop_back();
    					ans++;
    					size--;
    				}
    				break;
    			}
    			case 'F':{
    				if(size<k) printf("-1
    ");
    				else printf("%d
    ",q[k-1]+delta);
    				break;
    			}
    	}
    	}
    	printf("%d
    ",ans);
    return 0;
    }
    

    第二种方法:splay

    #include<cstdio>
    #define maxn 100009
    using namespace std;
    struct splay{
        int l, r, fa, size, v, time;
    }node[maxn];
    int root = 0, cnt = 0, change = 0, p = 0, n, small;
    inline void update(int x){node[x].size = node[node[x].l].size + node[node[x].r].size + node[x].time;}
    void zig(int x){
        int F = node[x].fa, G = node[F].fa;
        if (G){
            if (node[G].l == F) node[G].l = x;
            else node[G].r = x;
        }
        node[x].fa = G;
        node[F].l = node[x].r;
        if (node[x].r) node[node[x].r].fa = F;
        node[F].fa = x;
        node[x].r = F;
    }
    void zag(int x){
        int F = node[x].fa, G = node[F].fa;
        if (G){
            if (node[G].l == F) node[G].l = x;
            else node[G].r = x;
        }
        node[x].fa = G;
        node[F].r = node[x].l;
        if (node[x].l) node[node[x].l].fa = F;
        node[F].fa = x;
        node[x].l = F;
    }
    void Splay(int x, int s = 0){
        while (node[x].fa != s){
            int F = node[x].fa, G = node[F].fa;
            if (G == s){
                if (node[F].l == x) zig(x); else zag(x);
                update(F);
                break;
            }
            else{
                if (node[F].l == x){
                    if (node[G].l == F){zig(F); zig(x);}
                    else{zig(x);zag(x);}
                }
                else{
                    if (node[G].r == F){zag(F); zag(x);}
                    else{zag(x); zig(x);}
                }
                update(G);
                update(F);
            }
        }
        update(x);
        if (!s) root = x;
    }
    int find(int x){
        int p = root;
        while (p && node[p].v != x){
        if (x > node[p].v) p = node[p].r; else p = node[p].l;
        }
        if (p) Splay(p);
        return p;
    }
    void insert(int x){
        if (!root){node[++cnt] = (splay){0, 0, 0, 1, x, 1}; root = cnt; return;}
        int p = root, p2;
        while (p){
            if (x == node[p].v) {++node[p].time;update(p); Splay(p);break;}
            else if (x > node[p].v) p2 = node[p].r;
            else p2 = node[p].l;
            if (!p2){
                node[++cnt] = (splay){0, 0, p, 1, x, 1};
                if (x > node[p].v) node[p].r = cnt;
                else node[p].l = cnt;
                update(p);
                Splay(cnt);
                break;
            }
            p = p2;
        }
    }
    int getKth(int k){
        int p = root;
        while (k){
            int size = node[node[p].l].size, time = node[p].time;
            if (size + time < k) {p = node[p].r; k -= size + time;}
            else if (size >= k) p =  node[p].l;
            else return node[p].v;
        }
    }
    void del(){
        int p = root, p2 = root, ch = small - change;
        while(p){
            if (node[p].v < ch) p = node[p].r;
            else{p2 = p; p = node[p].l;}
        }
        if (node[p2].v < ch) {root = 0;return;}
        Splay(p2);
        node[p2].l = 0;
        update(p2);
    }
    int read(){
        char c;
        while (c = getchar(), c < '0' || c > '9');
        int x = c - '0';
        while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0';
        return x;
    }
    int main(){
        char c; int x;
        node[0] = (splay){0, 0, 0, 0, 0, 0};
        n = read(); small = read();
        while(n--){
            while (c = getchar(), !(c >= 'A' && c <= 'Z'));
            x = read();
            switch(c){
                case 'I':if (x >= small) {++p;insert(x - change);};break;
                case 'A':change += x;break;
                case 'S':change -= x; del();break;
                case 'F':if (x > node[root].size) printf("-1
    ");
                else printf("%d
    ", getKth(node[root].size - x + 1) + change);break;
            }
        }
        printf("%d
    ", p - node[root].size);
        return 0;
    } 
    

      

    1568:普通平衡树

    这是一道模板题。

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

    1、插入 xx 数;

    2、删除 xx 数(若有多个相同的数,因只删除一个);

    3、查询 xx 数的排名(若有多个相同的数,因输出最小的排名);

    4、查询排名为 xx 的数;

    5、求 xx 的前趋(前趋定义为小于 xx,且最大的数);

    6、求 xx 的后继(后继定义为大于 xx,且最小的数)。

    treap的模板

    https://www.cnblogs.com/Y-Knightqin/p/12252930.html 
    #include <bits/stdc++.h>
    #define maxn 100010
    #define inf 0x3f3f3f3f
    using namespace std;
    //https://www.cnblogs.com/Y-Knightqin/p/12252930.html 
    int key[maxn],cnt[maxn],ran[maxn],siz[maxn],son[maxn][2],op,n,num=0,x,root=0;
    void pushup(int x)
    {
        siz[x]=siz[son[x][0]]+siz[son[x][1]]+cnt[x];
    }
    void rotate(int &x,int op)
    {
        int p=son[x][!op];
        son[x][!op]=son[p][op];
        son[p][op]=x;
        pushup(x);
        pushup(p);
        x=p;
    }
    void insert(int &k,int x)
    {
        if (k==0)
        {
            k=++num;
            cnt[k]=1;
            key[k]=x;
            ran[k]=rand();
            siz[k]=1;
            return;
        }
        else if (key[k]==x)
        {
            cnt[k]++;
            siz[k]++;
            return;
        }
        int op=(x>key[k]);
        insert(son[k][op],x);
        if (ran[son[k][op]]<ran[k]) rotate(k,!op);
        pushup(k);
    }
    void _delete(int &k,int x)
    {
        if (k==0) return;
        if (x!=key[k])
        {
            int op=(x>key[k]);
            _delete(son[k][op],x);
            pushup(k);
            return;
        }
        if (cnt[k]>1)
        {
            cnt[k]--;
            siz[k]--;
            pushup(k);
            return;
        }
        if (!son[k][0]&&son[k][1])
        {
            rotate(k,0);
            _delete(son[k][0],x);
        }
        else if (son[k][0] && !son[k][1])
        {
            rotate(k,1);
            _delete(son[k][1],x);
        }
        else if (!son[k][0] && !son[k][1])
        {
            cnt[k]--;
            siz[k]--;
            if (cnt[k]==0) k=0;
        }
        else
        {
            int op=(ran[son[k][0]]>ran[son[k][1]]);
            rotate(k,!op);
            _delete(son[k][!op],x);
        }
        pushup(k);
    }
    int rank(int k,int x)
    {
        if (k==0) return 0;
        if (key[k]==x) return siz[son[k][0]]+1;
        if (key[k]>x) return rank(son[k][0],x);
        return siz[son[k][0]]+cnt[k]+rank(son[k][1],x);
    }
    int find(int k,int x)
    {
        if (k==0) return 0;
        if (siz[son[k][0]]>=x) return find (son[k][0],x);
        else if (siz[son[k][0]]+cnt[k]<x) return find (son[k][1],x-siz[son[k][0]]-cnt[k]);
        else return key[k];
    }
    int lowerbound(int k,int x)
    {
        if (k==0) return -inf;
        if (key[k]>=x) return lowerbound(son[k][0],x);
            else return max(key[k],lowerbound(son[k][1],x));
    }
    int upperbound(int k,int x)
    {
        if (k==0) return inf;
        if (key[k]<=x) return upperbound(son[k][1],x);
            else return min(key[k],upperbound(son[k][0],x));
    }
    int main()
    {
        cin>>n;
        while (n--)
        {
            scanf("%d%d",&op,&x);
            switch(op)
            {
                case 1:insert(root,x);break;
                case 2:_delete(root,x);break;
                case 3:printf("%d
    ",rank(root,x));break;
                case 4:printf("%d
    ",find(root,x));break;
                case 5:printf("%d
    ",lowerbound(root,x));break;
                case 6:printf("%d
    ",upperbound(root,x));break;
            }
        }
        return 0;
     }
    

      里面还写了一个懒人版treap,用vector实现的

    #include <bits/stdc++.h>
    #define debug freopen("r.txt","r",stdin)
    #define mp make_pair
    #define ri register int
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 4e5+5;
    const int INF = 0x3f3f3f3f; 
    const int mod = 998244353;
    inline ll read(){ll s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;}
    ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;}
    vector<int>v;
    int t,opt,x;
    int main()
    {
        t=read();
        while(t--)
        {
            opt=read(),x=read();
            if(opt==1) v.insert(lower_bound(v.begin(),v.end(),x),x);
            if(opt==2) v.erase (lower_bound(v.begin(),v.end(),x));
            if(opt==3) printf("%d
    ",lower_bound(v.begin(),v.end(),x)-v.begin()+1);
            if(opt==4) printf("%d
    ",v[x-1]);
            if(opt==5) printf("%d
    ",v[lower_bound(v.begin(),v.end(),x)-v.begin()-1]);
            if(opt==6) printf("%d
    ",v[upper_bound(v.begin(),v.end(),x)-v.begin()]);
        }
        return 0;
    }
    

      

  • 相关阅读:
    C# 动态添加用户控件
    临时记载C#中给DataGrid添加甘特图效果
    xp去掉快捷方式上的箭头
    wpf DataGrid的操作
    一路向前的算法
    WPF 解决WebBrowser控件汉字出现乱码的一种方案
    在CentOS 7 上安装 WordExpress
    创建Chrome插件
    开始PHP和JAVA
    failedtoloadcbsonextension
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/13775997.html
Copyright © 2020-2023  润新知