(可持久化无旋Treap / stl)

例:UVA - 12538 Version Controlled IDE
1、利用可持久化无旋Treap
有旋treap是不能维护给定序列的,但是无旋treap可以,但是对于可持续化,会消耗很大的空间
无旋treap在维护给定顺序的序列中,其序列下标满足BST,如果不是维护给定顺序的序列,那么其节点值满足BST
这个题是要求维护给定顺序的,剩下一种传送门
指针版本的,是我阅读能力太差了,看不懂,巨犇的代码技巧看不懂,最后选择了使用数组版本

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

const int maxn = 50010;

char s[maxn * 100];

struct tree
{
    int lch,rch,pri,sze;
    char val;
}node[maxn * 100];//因为是可持久化数据,所以数组要开大

int cnt,root[maxn],d,now,a,b,c;
//a/b/c 记录分裂后子树的根节点
//d:记录已经输出的c的个数
//now : 目前版本


//每次分裂或者合并就要更新当前根节点下树的大小
void update(int rt)
{
    node[rt].sze = 1;
    if(node[rt].lch){
        node[rt].sze += node[node[rt].lch].sze;
    }
    if(node[rt].rch){
        node[rt].sze += node[node[rt].rch].sze;
    }
}

int newnode(char w = 0)
{
    cnt++;
    node[cnt].val = w;
    node[cnt].lch = node[cnt].rch = 0;
    node[cnt].pri = rand();
    node[cnt].sze = 1;
    return cnt;
}

//合并操作,模板
//这里默认x树节点的值的序号全部小于y树节点的值的序号
int merge(int x,int y)
{
    if(!x||!y){
        return x + y;
    }
    int rt = newnode();
    if(node[x].pri < node[y].pri){
        node[rt] = node[x];
        node[rt].rch = merge(node[rt].rch,y);
    }
    else{
        node[rt] = node[y];
        node[rt].lch = merge(x,node[rt].lch);
    }
    update(rt);
    return rt;
}

//分裂操作,模板
//分裂成x的树中所有节点的序号都小于y树中所有节点的序号值
void split(int rt,int k,int &x,int &y)
{
    if(!rt){
        x = y = 0;
    }
    else{
        if(k <= node[node[rt].lch].sze){
            y = newnode();
            node[y] = node[rt];
            split(node[y].lch,k,x,node[y].lch);
            update(y);
        }
        else{
            x = newnode();
            node[x] = node[rt];
            split(node[x].rch,k - node[node[rt].lch].sze - 1,node[x].rch,y);
            update(x);
        }
    }
}

//建树操作
void build(int l,int r,int &rt)
{
    if(l > r){
        return ;
    }
    int m = (l + r)>>1;
    rt = newnode(s[m]);
    build(l,m - 1,node[rt].lch);
    build(m + 1,r,node[rt].rch);
    update(rt);
}


//插入操作
void ins(int &rt,int last,int pos)
{
    // rt : 更新的版本根节点
	// last : 旧版本的根节点
	// pos : 要插入的位置
	a = b = c = 0;
	scanf("%s",s + 1);
	int len = strlen(s + 1);
	split(last,pos,a,c);
	build(1,len,b);
	//将插入完的树重新合并
	rt = merge(merge(a,b),c);
}

//删除操作
void del(int &rt,int last,int pos,int len)
{
    // rt : 更新的版本根节点
	// last : 旧版本的根节点
	// pos : 要删除的位置
	// len : 要删除的长度
	a = b = c = 0;
	split(last,pos - 1,a,b);
	split(b,len,b,c);
	// 分成三组,然后把前后两组合并(中间的是被删除的
	rt = merge(a,c);
}

//前序遍历二叉树
void print(int rt)
{
    if(!rt){
        return ;
    }
    print(node[rt].lch);
    printf("%c",node[rt].val);
    if(node[rt].val == 'c'){
        d++;//统计输出的c的数量
    }
    print(node[rt].rch);
}

void print(int rt,int pos,int len)
{
    // rt : 更新的版本根节点
	// pos : 要输出的起始位置
	// len : 要输出的长度
	a = b = c = 0;
	split(rt,pos - 1,a,b);
	split(b,len,b,c);
	//分离出我们想要的子树;
	print(b);
	printf("\n");
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--){
        a = b = c = 0;
        int oper;
        scanf("%d",&oper);
        if(oper == 1){
            int p;
            scanf("%d",&p);
            p -= d;
            ins(root[now + 1],root[now],p);
            //更新一次,版本数+1;
            now++;
        }
        if(oper == 2){
            int p,l;
            scanf("%d%d",&p,&l);
            p -= d,l -= d;
            del(root[now + 1],root[now],p,l);
            //更新一次,版本数+1;
            now++;
        }
        if(oper == 3){
            int ver,p,l;
            scanf("%d%d%d",&ver,&p,&l);
            ver -= d,p -= d,l -= d;
            print(root[ver],p,l);
        }
    }
    return 0;
}

2、利用c++的stl容器crope,内部是用平衡树实现的,操作都是O(logN)
这里直接转了别人的代码
代码出处传送门

#include<iostream>
#include<cstdio>
#include<ext/rope>
#include<string>

using namespace std;
using namespace __gnu_cxx;

const int maxn=50005;

crope ro,l[maxn],tmp;

char str[205];

int main()
{
    int n,op,p,cnt,c,d,v;
    d=0;
    cnt=1;
    scanf("%d",&n);
    while(n--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%s",&p,str);
            p-=d;
            ro.insert(p,str);
            l[cnt++]=ro;
        }else if(op==2){
            scanf("%d%d",&p,&c);
            p-=d;c-=d;
            ro.erase(p-1,c);
            l[cnt++]=ro;
        }else{
            scanf("%d%d%d",&v,&p,&c);
            v-=d;p-=d;c-=d;
            tmp=l[v].substr(p-1,c);
            d+=count(tmp.begin(),tmp.end(),'c');
            cout<<tmp<<endl;
        }
    }
    return 0;
}
/*
6
1 0 abcdefgh
2 4 3
3 1 2 5
3 3 3 4
1 4 xy
3 5 4 6
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值