【线段树】哨戒炮

本文探讨了哨戒炮问题,一种经典的区间信息维护问题,通过使用线段树进行高效解答。介绍了线段树的基本原理,如何通过维护四个关键值实现区间合并,以及具体的代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

哨戒炮(线段树区间信息维护)

【题目描述】
防线上有 N 个紧挨着排成一行的炮台,每一个炮台至多能够放置一个哨戒炮。在不同地炮 台放置哨戒炮能获得不同的防御力(防御力可以为负,表示有负的防御作用) 。特别地,初 始时第 i 个炮台放置哨戒炮能获得的防御力为pi。

由于哨戒炮工作时需要周围有足够的空间,相邻的两个炮台不能同时放置哨戒炮。有时,由于地形条件的变化炮台放置哨戒炮获得 的防御力会发生改变。现在,你需要写个程序来回答这样的询问:仅使用[l,r]区间内的炮 台放置哨戒炮能够获得的最大防御力总和是多少(可以一个都不放置,这时防御力总和为0) 。

【样例输入】
5 5
1 100 1 100 1
1 1 5
1 3 4
2 4 -1000
1 1 5
1 4 4
【样例输出】
200
100
101
0

线段树水题咳咳
首先如果暴力dp能过50分,即每次询问都做一次dp,时间复杂度O(nm)。
涉及修改的问题自然要用数据结构维护,这道题显然 使用线段树。那么现在的问题就在于区间信息合并。由于相邻的炮台不能同时选择(这也是这道题唯一的思维难点),故相邻区间的交界处的两个炮台需要判断是否选择。由此稍加 分析,我们就能轻易 得到一个方法:对于一个区间,维护四个值,就能进行区间合并——当前区间不选左端点的最大防御力,不选右端点的最大防御力,两端点都不选的最大防御力,以及当前区间的最大防御力。合并区间时,我们对于这四个值进行转移,问题就解决了。
详细合并方法见代码:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1) 
#define re register
using namespace std;
long long n,m;
long long a[100001];
struct tree{
	long long l,r;
	long long ml,mr,mm;//ml是不选右端点的最大防御力,mr是不选左端点的最大防御力,mm是都不选的最大防御力。 
	long long mx;//区间本身的最大防御力 
}t[400001];
inline tree merge(tree l,tree r)
{
	tree ans;
	ans.l=l.l;
	ans.r=r.r;
	//转移:可以画图,结合dp思想加以理解 
	ans.mm=max(l.mm+r.ml,r.mm+l.mr); 
	ans.ml=max(r.ml+l.ml,r.mm+l.mx);
	ans.mr=max(r.mr+l.mr,l.mm+r.mx);
	ans.mx=max(l.mx+r.mr,r.mx+l.ml);
	ans.mx=max(ans.mx,0ll);
	ans.mr=max(ans.mr,0ll);
	ans.ml=max(ans.ml,0ll);
	ans.mm=max(ans.mm,0ll);
	return ans;
}
inline void build(long long p,long long l,long long r)
{
	t[p].l=l;
	t[p].r=r;
	if(l==r)
	{
		t[p].mx=max(a[l],0ll);
		return;
	}
	long long mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	t[p]=merge(t[lc],t[rc]);
}
inline void change(long long p,long long x,long long v)
{
	long long l=t[p].l,r=t[p].r;
	if(t[p].l==t[p].r)
	{
		t[p].mx=max(0ll,a[l]);
		return;
	}
	long long mid=(l+r)>>1;
	if(x<=mid)change(lc,x,v);
	else change(rc,x,v);
	t[p]=merge(t[lc],t[rc]);
}
inline tree ask(long long p,long long ql,long long qr)
{
	if(ql<=t[p].l&&qr>=t[p].r)
	{
		return t[p];
	}
	long long mid=(t[p].l+t[p].r)>>1;
	if(ql<=mid&&qr>mid)
	{
		return merge(ask(lc,ql,qr),ask(rc,ql,qr));
	}
	else if(ql<=mid)return ask(lc,ql,qr);
	else if(qr>mid)return ask(rc,ql,qr);
}
long long f,b,c;
int main()
{
	scanf("%lld%lld",&n,&m);
	for(long long re i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	build(1ll,1ll,n);
	for(long long re i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&f,&b,&c);
		if(f==1)printf("%lld\n",ask(1ll,b,c).mx);
		else a[b]=c,change(1ll,b,c);
	}
	return 0;
} 

很简单吧! 但是这改变不了我读错题把a[b]=c写成a[b]+=c而WA很久的事实。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值