哨戒炮(线段树区间信息维护)
【题目描述】
防线上有 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很久的事实。。。。。