zoukankan      html  css  js  c++  java
  • 浅谈如何优美地实现线段树?

    前言

    线段树作为一种基础的数据结构,在信息学竞赛中有着广泛的应用。

    与之相伴随的是大量的基于线段树维护的题目。

    在这其中,有不少题目所需要实现的线段树包含多种操作,维护多种信息,拥有较大的代码难度。

    相信不少人也有过写线段树越写越乱,写大量本质相同代码的经历。

    笔者在此结合平日里实现线段树的经验,斗胆浅谈一下如何准确,快速的实现线段树。

    正文

    首先,在笔者的平时习惯中。线段树的写法通常根据节点信息是否用结构体封装可以分为两类。

    这里强烈建议读者通过练习做到熟悉两种不同的线段树写法。

    从而在题目较为简单时,使用第一类线段树快速实现。

    而在操作较为繁琐,询问多种信息时,则采取第二种线段树,提高代码准确度。

    这里第一类线段树的实现较为简单,不在赘述,重点讲解第二类线段树的实现。

    这里首先给出第二类线段树的框架

    struct tag
    {
    	//the tag of each node
    	void clear(){}
    };
    tag operator+=(tag &x,tag k)
    {
    	tag nx=x;
    	/*
    		...
    	*/
    	return x=nx;
    }
    struct data
    {
    	//the data of each node
    };
    data operator+(data a,data b)
    {
    	data ans;
    	/*
    		...
    	*/
    	return ans;
    }
    data operator+=(data &x,tag k)
    {
    	data nx=x;
    	/*
    		...
    	*/
    	return x=nx;
    }
    struct Segment_Tree
    {
    	#define lson o<<1
    	#define rson o<<1|1
    	#define mid ((t[o].l+t[o].r)>>1)
    	tag f[N*4];
    	data t[N*4];
    	inline void pushup(ll o){t[o]=t[lson]+t[rson];}
    	inline void update(ll o,tag k){f[o]+=k,t[o]+=k;}
    	inline void pushdown(ll o){update(lson,f[o]);update(rson,f[o]);f[o].clear();}
    	void build(ll o,ll l,ll r)
    	{
    		f[o].clear();t[o].l=l;t[o].r=r;t[o].len=r-l+1;
    		if(l==r)return (void)(t[o]={l,r,r-l+1,});
    		build(lson,l,mid);build(rson,mid+1,r);pushup(o);
    	}
    	void opt(ll o,ll ql,ll qr,ll k)
    	{
    		if(ql<=t[o].l&&t[o].r<=qr)return update(o,{});
    		pushdown(o);
    		if(ql<=mid)optadd(lson,ql,qr,k);
    		if(qr>mid)optadd(rson,ql,qr,k);
    		pushup(o);
    	}
    	data query(ll o,ll ql,ll qr)
    	{
    		if(ql<=t[o].l&&t[o].r<=qr)return t[o];
    		pushdown(o);
    		bool fg1=ql<=mid,fg2=qr>mid;
    		if(fg1&&fg2)return query(lson,ql,qr)+query(rson,ql,qr);
    		else
    		{
    			if(fg1)return query(lson,ql,qr);
    			if(fg2)return query(rson,ql,qr);
    		}
    	}
    }T;
    

    简单介绍一下实现的思想。
    首先将每个节点所存储的信息分为(tag)(data),分别对应"懒标记"和"维护信息"。
    然后我们需要做的便是重载以下三种运算符

    (1.tag+=tag)
    (2.data+data)
    (3.data+=tag)

    这三种运算符分别对应了
    1.父节点的标记作用于子节点的标记
    2.左儿子信息与右儿子信息的合并
    3.父节点的标记作用于子节点的存储信息

    有了这三种运算符以后,我们便将几乎一切线段树的实现问题转化为了定义这三种运算+复制模板这一问题。

  • 相关阅读:
    HDU
    HDU
    HDU
    HDU
    HDU
    P6146 [USACO20FEB]Help Yourself G 组合数学 DP
    CodeForces
    POJ
    【网络学习】集线器,交换机,路由器的作用
    【Python学习】深拷贝和浅拷贝
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/14757704.html
Copyright © 2011-2022 走看看