【数据结构】主席树与一般线段树的比较

目录

背景

线段树

主席树

结语


背景

    明天是JSOI2019省选R1D1。今天考虑到近期数据结构手生了,决定把几种线段树理一下。

    在这个过程中,有两点发现。其一是在某些实现下,可持久化的数据结构往往只需要普通的数据结构加入几行代码即可。这就意味着,可持久化的本质就应当是由基础数据结构适当记录更改而实现的。

    当然,像这样的普通数据结构,它越接近于算法的本质(比如树形用指针,而不是用数组,甚至是用一些粗略的计算来确定位置),就越容易进行可持久化的更改。

    这或许也暗示着我们,发展的前提,是抓住本质。

线段树

    这是一棵可爱的,能支持区间加和区间求和的线段树。

    区间修改以及区间求最值,并没有多大本质区别,故未写。

struct Segment_tree_type3{//just like president tree
	struct node{
		int ls,rs,he,p;
	};
	node t[MAXN*4+1];
	int tlen;
	int build()
	{
		qing(tlen = 1);
	}
	int qing(int cn)
	{
		t[cn].ls = t[cn].rs = t[cn].he = t[cn].p = 0;
		return cn;
	}
	void jia(int cn,int cl,int cr,int cm,int l,int r)
	{
		if(cl<=l && r<=cr){
			t[cn].p += cm;
			t[cn].he += cm * (r-l+1);
			return;
		}
		tui(cn,l,r);
		if(cl <= (l+r)/2)jia(t[cn].ls,cl,cr,cm,l,(l+r)/2);
		if(cr > (l+r)/2)jia(t[cn].rs,cl,cr,cm,(l+r)/2+1,r);
		update(cn);
	}
	int qiu(int cn,int cl,int cr,int l,int r)
	{
		if(cl<=l && r<=cr)return t[cn].he;
		tui(cn,l,r);
		int guo = 0;
		if(cl <= (l+r)/2)guo += qiu(t[cn].ls,cl,cr,l,(l+r)/2);
		if(cr > (l+r)/2)guo += qiu(t[cn].rs,cl,cr,(l+r)/2+1,r);
		return guo;
	}
	void tui_e(int cn,int &cm,int len)
	{
		if(!cm)cm = qing(++tlen);
		t[cm].p += t[cn].p;
		t[cm].he += len * t[cn].p;
	}
	void tui(int cn,int l,int r)
	{
		int zh = (l+r)/2;
		tui_e(cn,t[cn].ls,zh-l+1);
		tui_e(cn,t[cn].rs,r-zh);
		t[cn].p = 0;
	}
	void update(int cn)
	{
		t[cn].he = t[t[cn].ls].he + t[t[cn].rs].he;
	}
};

主席树

    而这是一棵同样可爱的,同样支持区间加和区间求和的主席树。

struct Zxs_type2{//basic president tree, can change a range
	struct node{
		int ls,rs,he,p;
	};
	node t[MAXN*40+1];
	int tlen;
	void build()
	{
		qing(tlen = 1);
	}
	int qing(int cn)
	{
		t[cn].ls = t[cn].rs = t[cn].he = t[cn].p = 0;
		return cn;
	}
	void jia(int cn,int cl,int cr,int cm,int l,int r)
	{
		if(cl <= l && r<=cr){
			t[cn].p += cm;
			t[cn].he += cm * (r-l+1);
			return;
		}
		tuo(cn);
		tui(cn,l,r);
		if(cl <= (l+r)/2)jia(t[cn].ls,cl,cr,cm,l,(l+r)/2);
		if(cr > (l+r)/2)jia(t[cn].rs,cl,cr,cm,(l+r)/2+1,r);
		update(cn);
	}
	void qiu(int cn,int cl,int cr,int l,int r)
	{
		if(cl<=l && r<=cr)return t[cn].he;
		tui(cn,l,r);
		int guo = 0;
		if(cl <= (l+r)/2)guo += qiu(t[cn].ls,cl,cr,l,(l+r)/2);
		if(cr > (l+r)/2)guo += qiu(t[cn].rs,cl,cr,(l+r)/2+1,r);
		return guo;
	}
	inline void tui(int cn,int l,int r)
	{
		int zh = (l+r)/2;
		tui_e(cn,t[cn].ls,zh-l+1);
		tui_e(cn,t[cn].rs,r-zh);
		t[cn].p = 0;
	}
	inline void tui_e(int cn,int cm,int len)
	{
		t[cm].p += t[cn].p;
		t[cm].he += t[cn].p * len;
	}
	void update(int cn)
	{
		t[cn].he = t[t[cn].ls].he + t[t[cn].rs].he;
	}
	inline void tuo_e(int cn,int &cm)
	{
		t[cm = qing(++tlen)] = t[cn];
	}
	inline void tuo(int cn)
	{
		tuo_e(t[cn].ls,t[cn].ls);
		tuo_e(t[cn].rs,t[cn].rs);
	}
};

    可以看到,我的这一棵主席树,比前面的线段树多的只是三处:

        1、t数组大小由4倍改到40倍

        2、在jia(区间加)函数中,下传标记(tui)前先创建新点(tuo)

        3、在最末尾的地方多了两个用来创建新点的函数(tuo和tuo_e)

结语

    个人认为这样子来学习可持久化算法,可以很好地在考场上临时添加可持久化功能,也能较为深刻地理解可持久化究竟干了什么。最关键的是,它会使代码更便于调试与增加新功能。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值