线段树你能回答这问题吗?

Posted 行码棋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树你能回答这问题吗?相关的知识,希望对你有一定的参考价值。

思路:

线段树节点需要存储六个信息,连续区间和的最大值,连续前缀和的最大值,连续后缀和的最大值

最大连续区间和可能横跨左子区间和右子区间,当横跨时,区间和就为左子区间的最大后缀和加上右子区间的最大前缀和,然后再和左右区间的最大连续区间和取最大值,即
t r [ u ] . m x = m a x ( m a x ( t r [ u < < 1 ] . m x , t r [ u < < 1 ∣ 1 ] . m x ) , t r [ u < < 1 ] . r m a x + t r [ u < < 1 ∣ 1 ] . l m a x ) ; tr[u].mx = max(max(tr[u<<1].mx,tr[u<<1|1].mx),tr[u<<1].rmax+tr[u<<1|1].lmax); tr[u].mx=max(max(tr[u<<1].mx,tr[u<<11].mx),tr[u<<1].rmax+tr[u<<11].lmax);

计算最大前缀和和最大后缀和时,我们需要用到当前区间和的这个信息,需要定义sum这个信息,如果当前最大前缀和横跨的区间大于左子区间的长度,那么前缀和就为左子区间的总和加上右子区间的最大前缀和。计算最大后缀和时是同样的方法思路。即
t r [ u ] . l m a x = m a x ( t r [ u < < 1 ] . s u m + t r [ u < < 1 ∣ 1 ] . l m a x , t r [ u < < 1 ] . l m a x ) tr[u].lmax = max(tr[u<<1].sum+tr[u<<1|1].lmax,tr[u<<1].lmax) tr[u].lmax=max(tr[u<<1].sum+tr[u<<11].lmax,tr[u<<1].lmax)
t r [ u ] . r m a x = m a x ( t r [ u < < 1 ] . r m a x + t r [ u < < 1 ∣ 1 ] . s u m , t r [ u < < 1 ∣ 1 ] . r m a x ) tr[u].rmax = max(tr[u<<1].rmax+tr[u<<1|1].sum,tr[u<<1|1].rmax) tr[u].rmax=max(tr[u<<1].rmax+tr[u<<11].sum,tr[u<<11].rmax)

建树时注意更新区间信息 [ l , r ] [l,r] [l,r],然后还需要向上更新值(由子节点更新父节点)

询问的时候,如果区间横跨左右两个子区间,需要取左子区间的后缀和和右子区间的前缀和求,因此节点的所有的信息都要更新(需要用到之前更新过的节点信息)

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;

int n,m;
int w[N];
struct node
{
	int l,r;
	int mx,lmax,rmax,sum;
}tr[N * 4];

void pushup(int u)
{
	tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
	tr[u].lmax = max(tr[u<<1].sum+tr[u<<1|1].lmax,tr[u<<1].lmax);
	tr[u].rmax = max(tr[u<<1].rmax+tr[u<<1|1].sum,tr[u<<1|1].rmax);
	tr[u].mx = max(max(tr[u<<1].mx,tr[u<<1|1].mx),tr[u<<1].rmax+tr[u<<1|1].lmax);
}
void build(int u,int l,int r)
{
	if(l==r) tr[u] = {l,r,w[r],w[r],w[r],w[r]};
	else
	{
	    tr[u] = {l,r};
		int mid = tr[u].l + tr[u].r >> 1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		pushup(u);
	}
}

node query(int u,int l,int r)
{
	if(tr[u].l>=l && tr[u].r<=r) return tr[u];
	else
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if(r<=mid) return query(u<<1,l,r);
		else if(l>mid) return query(u<<1|1,l,r);
		else 
		{
			auto left = query(u<<1,l,r);
			auto right = query(u<<1|1,l,r);
			node res;
			res.sum = left.sum + right.sum;
        	res.lmax = max(left.sum+right.lmax,left.lmax);
        	res.rmax = max(left.rmax+right.sum,right.rmax);
			res.mx = max(max(left.mx,right.mx),left.rmax+right.lmax);
			return res;
		}
	}
}

void modify(int u,int x,int v)
{
	if(tr[u].l==x && tr[u].r==x) tr[u]={x,x,v,v,v,v};
	else 
	{
		int mid = tr[u].l + tr[u].r >> 1;
		if(x<=mid) modify(u<<1,x,v);
		else modify(u<<1|1,x,v);
		pushup(u);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	build(1,1,n);
	
	int k,x,y;
	while(m--)
	{
		scanf("%d%d%d",&k,&x,&y);
		if(k==1)
		{
			if(x>y) swap(x,y);
			printf("%d\\n",query(1,x,y).mx);
		}
		else modify(1,x,y);
	}
	return 0;
}

以上是关于线段树你能回答这问题吗?的主要内容,如果未能解决你的问题,请参考以下文章

你能回答这些问题吗 (线段树)

面试官:你能回答这两个简单的问题吗

线段树入门+例题

完爆面试官!面试常常死在这几个问题上,面试官问题背后的潜台词你能听懂吗?

ACM入门之线段树习题

面试官问:如何优化高并发相关的业务,你能回答的上来吗?