数据结构(终极线段树篇)

数据结构(终极线段树篇)

摘要:

问题的提出:如何解决多样化的区间操作问题?

solve:线段树!!!

关键字:

线段树,可持久化线段树,权值线段树,线段树森林,动态开点线段树,区间操作,线段树应用。

前言:

区间操作问题的解决方法极多,如:树状数组,RMQ等。

所有这些数据结构都具有一定的局限性,都具有各自的优势和劣势。

而线段树无疑是这些数据结构中性价比最高的数据结构!(Ps:搞什么线段树,暴力出奇迹啊!


线段树,线段树,线段树。

顾名思义:树上每一个节点表示一个线段(区间),每一次对一整个线段(区间)进行操作,棒棒棒!!!

        第1节讲述了线段树的基本性质。

        第2节实现了基本线段树。

        第3节讲述动态开点线段树。

        //第4节讲述权值线段树。

        //第5节讲述线段树森林。

        //第6节讲述线段树的重要应用和例题。


1.线段树的基本性质:

        1.0基本线段树的性质:

           1.每个节点u表示一个区间[l,r]。若节点u非叶子节点,则u有两个儿子。设(mid=(l+r)/2)。                                                 左儿子为[l,mid],节点编号为u*2。                                                                                                                                   右儿子为[mid+1,r],节点编号为u*2+1。

            2.线段树的深度为O(lgn)

            3.线段树的节点个数为O(n),常数约为4。

            4.线段树上任意一个区间[l,r]都可以被O(logn)个线段覆盖。

            

             思考:为何左儿子不为[l,mid+1],为何左儿子的节点编号为u*2。

             思考:如何证明线段树的节点个数为O(n),且常数为4。

             思考:如何证明线段树上任意一个区间[l,r]都可以被O(logn)个线段覆盖。


倘若我们只需要单点修改,我们只需要找到相应的叶子节点,更改叶子节点并沿路更新。

但倘若我们需要区间修改,我们却需要引入lazy tag(懒惰标记)。

每一次操作都先下放懒标记,清空标记,再进行相应的操作。


如上图:若将[1,5]全加上3。

需要标记tag[1,5]=3,下一次操作到[1,5]时下放。

tag[1,3]=3,tag[4,5]=3,tag[1,5]=0,sum[1,5]+=3*5;


2.基本线段树的实现:(建议读者自行思考 

        2.1基本线段树的区间加,询问区间和:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1000005;
ll a[MAXN];
ll read() 
{
    char c=getchar(); ll f=1,x=0;
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}
    return f*x;
}
struct Segment_tree
{
	struct node
	{
		int l,r;
		ll sum,tag;
	} tree[MAXN<<2];
	void up(int x){ tree[x].sum=tree[x<<1].sum+tree[(x<<1)|1].sum; }
	void down(int x)
	{
		int num1=(tree[x<<1].r-tree[x<<1].l+1);
		int num2=(tree[(x<<1)|1].r-tree[(x<<1)|1].l+1);
		tree[(x<<1)|1].sum+=tree[x].tag*num2;  tree[x<<1].tag+=tree[x].tag;      
		tree[(x<<1)|1].tag+=tree[x].tag;       tree[x<<1].sum+=tree[x].tag*num1; 
		tree[x].tag=0;
	}
    void build(int x,int l,int r,ll *a)
    {
    	tree[x].l=l;
    	tree[x].r=r;
    	if (l==r)
    	{
    		tree[x].sum=a[l];
    		tree[x].tag=0;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(x<<1,l,mid,a);
        build((x<<1)|1,mid+1,r,a);
        up(x);
	}
	void change(int x,int l,int r,ll y)
	{
		if (tree[x].l>=l&&tree[x].r<=r)
		{
			tree[x].tag+=y;
			tree[x].sum+=y*(tree[x].r-tree[x].l+1);
			return;
	    }
	    down(x);
	    int mid=(tree[x].l+tree[x].r)>>1;
	    if (r<=mid) change(x<<1,l,r,y);
	    else if (l>mid) change((x<<1)|1,l,r,y);
	    else 
	    {
	    	change(x<<1,l,mid,y);
	    	change((x<<1)|1,mid+1,r,y);
		}
		up(x);
	}
	ll query(int x,int l,int r)
	{
	    if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;
	    down(x);
	    int mid=(tree[x].l+tree[x].r)>>1;
	    if (r<=mid) return query(x<<1,l,r);
	    else if (l>mid) return query((x<<1)|1,l,r);
	    else return query(x<<1,l,mid)+query((x<<1)|1,mid+1,r);
	}
	void print(int x)
	{
		for (int i=1;i<=x;i++) 
		printf("%d %d %d\n",tree[i].l,tree[i].r,tree[i].sum);
		printf("\n");
	}
} segment_tree;
int main()
{
	int n=read(),m=read();
	for (int i=1;i<=n;i++) a[i]=read();
	segment_tree.build(1,1,n,a);
	for (int i=1;i<=m;i++)
	{
		int c=read();
		if (c==1)
		{
			int x=read(),y=read(),z=read();
			segment_tree.change(1,x,y,z);
		}
		else
		{
			int x=read(),y=read();
			printf("%lld\n",segment_tree.query(1,x,y));
		}
	}
	return 0;
}

 
 

3.动态开点线段树

动态开点线段树,实现单点加,区间求和。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1000005; const int MAXS=1e9;
ll read() 
{
    char c=getchar(); ll f=1,x=0;
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}
    return f*x;
}
struct Dynamic_segment_tree
{
	int nodenum=0;
	struct node
	{
		int l,r,leftnode,rightnode;
		ll sum;
	} tree[MAXN<<2];
	void change(int &x,int l,int r,int t,ll y)
	{
		if (!x)
		{
			nodenum++;
			x=nodenum;
		}
		tree[x].l=l;
		tree[x].r=r;
		tree[x].sum+=y;
		if (l==r) return;
	    int mid=(tree[x].l+tree[x].r)>>1;
	    if (t<=mid) change(tree[x].leftnode,l,mid,t,y);
	    else change(tree[x].rightnode,mid+1,r,t,y);
	}
	ll query(int x,int l,int r)
	{
	    if (!x) return 0;
	    if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;
	    int mid=(tree[x].l+tree[x].r)>>1;
	    if (r<=mid) return query(tree[x].leftnode,l,r);
	    else if (l>mid) return query(tree[x].rightnode,l,r);
	    else return query(tree[x].leftnode,l,mid)+query(tree[x].rightnode,mid+1,r);
	}
	void print(int x)
	{
		for (int i=1;i<=x;i++) 
		printf("%d %d %d %d %d\n",tree[i].l,tree[i].r,tree[i].leftnode,tree[i].rightnode,tree[i].sum);
		printf("\n");
	}
} dynamic_segment_tree;
int main()
{
	int n=read(),m=read(),root=0;
	for (int i=1;i<=m;i++)
	{
		int c=read(),x=read(),y=read();
		if (c==1) dynamic_segment_tree.change(root,1,MAXS,x,y);
		else printf("%d\n",dynamic_segment_tree.query(root,x,y));
		//dynamic_segment_tree.print(20);
	}
	return 0;
}

之后的内容将在7月前全部完成。

猜你喜欢

转载自blog.csdn.net/xmr_pursue_dreams/article/details/80316611