首页 > 其他分享 >线段树

线段树

时间:2024-02-20 12:11:25浏览次数:21  
标签:rt val int 线段 tr sum rson

线段树算是树状数组的进阶版了,树状数组能做的,线段树也肯定能做,它做不了的,线段树也能做。
但确实线段树的代码量也很让人挠头,基本原理不再解释。
先看一下基础的模板吧

单点修改和区间查询

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=300000;
int n,m;
int a[N];
#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{
	int l,r;
	int sum,max;
}tr[N<<2];

void build(int rt,int l,int r)//建树 
{
	tr[rt].l=l;
	tr[rt].r=r;
	if(l==r)
	{
		tr[rt].sum=a[l];
		tr[rt].max=a[l];
		return ; 
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	tr[rt].sum=tr[lson].sum+tr[rson].sum;
	tr[rt].max=max(tr[lson].max,tr[rson].max);
}

void update(int rt,int k,int val)//单点修改 
{
	if(tr[rt].l==tr[rt].r)
	{
		tr[rt].sum+=val;
		tr[rt].max+=val;
		return ;
	}
	int mid=(tr[rt].l+tr[rt].r)>>1;
	if(k<=mid)
	{
		update(lson,k,val);
	}
	if(k>mid)
	{
		update(rson,k,val);
	}
	tr[rt].sum=tr[lson].sum+tr[rson].sum;
	tr[rt].max=max(tr[lson].max,tr[rson].max);
}

int query1(int rt,int l,int r)//区间查询,求和 
{
	if(l<=tr[rt].l&&r>=tr[rt].r)
	{
		return tr[rt].sum;
	}
	int mid=(tr[rt].l+tr[rt].r)>>1;
	int val=0;
	if(l<=mid)
	{
		val+=query1(lson,l,r);
	}
	if(r>mid)
	{
		val+=query1(rson,l,r);
	}
	return val;
} 
int query2(int rt,int l,int r)//区间求max 
{
	if(l<=tr[rt].l&&r>=tr[rt].r)
	{
		return tr[rt].max;
	}
	int mid=(tr[rt].l+tr[rt].r)>>1;
	int val=0;
	if(l<=mid)
	{
		val=max(val,query2(lson,l,r));
	}
	if(r>mid)
	{
		val=max(val,query2(rson,l,r));
	}
	return val;
} 
int main()
{
	scanf("%d",&n);
	if(n!=0)
	{
		build(1,1,n);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		update(1,i,a[i]);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		char op[3];
		
		scanf("%s",op);
		
		if(op[0]=='A')
		{
			int k,val;
			scanf("%d%d",&k,&val);
			update(1,k,val);
		}
		if(op[0]=='S')
		{
			int l,r;
			scanf("%d%d",&l,&r);
			int ans=query1(1,l,r);
			printf("%d\n",ans);
		}
	}
}
 

区间修改和区间查询

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=300000;
int n,m;
int a[N];

#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{
	int l,r;
	int sum;
	int lazy;//延时标记 
}tr[N<<2]; 
void pushdown(int rt)//延时标记下放到左右子树 
{
	if(tr[rt].lazy)
	{
		int lz=tr[rt].lazy;
		tr[rt].lazy=0;
		tr[lson].lazy+=lz;
		tr[rson].lazy+=lz;
		tr[lson].sum+=lz*(tr[lson].r-tr[lson].l+1);
		tr[rson].sum+=lz*(tr[rson].r-tr[rson].l+1);
	}
}
void build(int rt,int l,int r)//建树 
{
	tr[rt].l=l;
	tr[rt].r=r;
	if(tr[rt].l==tr[rt].r)
	{
		tr[rt].sum=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	tr[rt].sum=tr[lson].sum+tr[rson].sum;
}
void update(int rt,int l,int r,int val)//区间修改 
{
	if(l<=tr[rt].l&&r>=tr[rt].r)
	{
		tr[rt].lazy+=val;
		tr[rt].sum+=val*(tr[rt].r-tr[rt].l+1);
		return ;
	}
	pushdown(rt);
	int mid=(tr[rt].l+tr[rt].r)>>1;
	if(l<=mid)
	{
		update(lson,l,r,val);
	}
	if(r>mid)
	{
		update(rson,l,r,val);
	}
	tr[rt].sum=tr[lson].sum+tr[rson].sum;
}
int query(int rt,int l,int r)//区间查询 
{
	if(l<=tr[rt].l&&tr[rt].r<=r)
	{
		return tr[rt].sum;
	}
	pushdown(rt);
	int mid=(tr[rt].l+tr[rt].r)>>1;
	int val=0;
	if(l<=mid)
	{
		val+=query(lson,l,r);
	}
	if(r>mid)
	{
		val+=query(rson,l,r);
	}
	return val;
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	build(1,1,n);
	scanf("%lld",&m);
	for(int i=1;i<=m;i++)
	{
		char s[3];
		scanf("%s",s);
		if(s[0]=='S')
		{
			int l,r;
			scanf("%lld%lld",&l,&r);
			printf("%lld\n",query(1,l,r));
		}
		if(s[0]=='A')
		{
			int l,r,val;
			scanf("%lld%lld%lld",&l,&r,&val);
			update(1,l,r,val);
		}
	}
}  
 

这里运用了一个延时标记,是为了节省时间的开销,每次只更新到所需的区间,如果还需要再下一层的区间就把延时标记下放。

看一个比较简单的应用

忠诚

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=300000;
int n,m;
int a[N];
#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{
	int l,r;
	int min;
}tr[N<<2]; 

void build(int rt,int l,int r)
{
	tr[rt].l=l;
	tr[rt].r=r;
	if(tr[rt].l==tr[rt].r)
	{
		tr[rt].min=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	tr[rt].min=min(tr[lson].min,tr[rson].min);
}
int query(int rt,int l,int r)
{
	if(l<=tr[rt].l&&r>=tr[rt].r)
	{
		return tr[rt].min;
	}
	int val=0x3f3f3f3f;
	int mid=(tr[rt].l+tr[rt].r)>>1;
	if(l<=mid)
	{
		val=min(val,query(lson,l,r));
	}
	if(r>mid)
	{
		val=min(val,query(rson,l,r));
	}
	return val;
}
int main()
{
	memset(tr,0x3f,sizeof(tr));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		int ans=query(1,l,r);
		printf("%d ",ans);
	}
}  
 

标签:rt,val,int,线段,tr,sum,rson
From: https://www.cnblogs.com/zhengchenxi/p/18022823

相关文章

  • "山海经“ 讲解----线段树
    ”山海经“--线段树讲解1、题面:http://cogs.pro/cogs/problem/problem.php?pid=7752、题目大意及分析:i:大概就是说给了你一段[1,n]的区间,并给了每个区间的权值,下面会有m个问题,每个问题给你一段[1,n]的子区间[i,j],问你在这段区间上的任意一端子区间和最大是多少,并且要求输出这......
  • 线段树的各种板板~(*^▽^*)
    $\color{purple}{板板}$#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineinf0x3f#defineINF0x3f3f3f3f#definemst(a,b)memset(a,b,sizeof(a))#defineElaina0#definelid(id<<1)#definerid(id<<1|1)//即ridco......
  • 线段树模板
    开局宏定义:#include<bits/stdc++.h>#defineintlonglong#definelson(now<<1)//现结点的左孩子#definerson(now<<1|1)//右孩子usingnamespacestd;结构体定义:structNode{intl,r;//表示左右区间intmax,sum;//其他数据域}tree[N<<2]//=N*......
  • 线段树(板子)
    线段树单点修改,单点,区间查询区间修改,单点,区间查询单点修改普通线段树code#definels(rt<<1)#definers(rt<<1|1)#definellllonglongconstintN=1000001;intn,m;inta[N];structT{ intl,r,data;}tr[N<<2];voidpushup(intrt){ tr[rt].da......
  • 线段树-基础模版
    线段树模版一埋头扎进线段树一上午感觉很神奇几个函数就能把它完整的复现下来这里有几张基础概念的图对着代码来想还是很好理解的最后是整理了能够处理总和最大值和最小值的线段树模版code:$\LARGE\color{purple}{代码在这里}$#include<bits/stdc++.h>usingnames......
  • 树状数组及线段树详解
    本文章是作者调了3个多小时后,感触颇深,历经1个多小时写出来的,不喜勿喷相关内容可参考模板总结中的树状数组及线段树;进入正题树状数组所谓树状数组,即像树的数组,一般应用于求多次前缀和以及区间前缀和的问题中;其根据节点编号的二进制数的末尾0的个数划分层次,每个节点的管辖......
  • 线段树进阶
    线段树进阶动态开点定义动态存储数据的线段树,可以优化空间复杂度实现为了避免\(N<<1\),不再使用完全二叉树存储,而记录左右儿子\(ls,rs\)此外需要\(tot\)记录已经开了多少点在递归时要记录点的左右区间,确保开点时能知道区间大小voidmodify(int&p,intL,intR,intl,i......
  • 线段树进阶
    线段树进阶线段树分治P5787判断二分图可以用扩展域并查集,采用线段树分治,线段树上的每一个结点作为每一段时间。每个结点上的修改和回溯需要用到可撤销的并查集。时间复杂度\(O(n\logk\logn)\)扫描线扫描线求矩形面积#include<bits/stdc++.h>usingnamespacestd;typ......
  • 线段树
    【前置知识】运算律,单位元。运算律基本就是交换律、结合律、分配律。单位元:假设我们现在有一个运算\(\bigoplus\)。如果\(a\bigoplusb=b\bigoplusa=a\),称\(b\)是运算\(\bigoplus\)的单位元。【线段树】线段树是一个树形数据结构。满二叉树;倍增的思想,分治的手......
  • 李超线段树
    李超线段树线段树的扩展版本用来动态维护平面上直线支持插入直线/线段,查询横坐标某处覆盖的线中纵坐标最大/最小值可以用于斜率优化DP插入直线这里直线是线段树上维护的信息,表示当前区间中点处最优的直线同时相当于区间修改,直线也是标记,这个区间内都可能有这条直线但是标......