VIJOS-P1083 小白逛公园
VIJOS-P1083 小白逛公园
Description
小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。 一开始,小白就根据公园的风景给每个公园打了分。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。 那么,就请你来帮小白选择公园吧。
Input
第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。 接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。 接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,小新要带小白出去玩,接下来的两个整数a和b给出了选择公园的范围(1≤a,b≤N);K=2表示,小白改变了对某个公园的打分,接下来的两个整数p和s,表示小白对第p个公园的打分变成了s(1≤p≤N)。 其中,1≤N≤500 000,1≤M≤100 000,所有打分都是绝对值不超过1000的整数。
Output
小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。
Sample Input
5 3 1 2 -3 4 5 1 2 3 2 2 -1 1 2 3
Sample Output
2 -1
题解:
题面不难理解,就是简单的动态单点修改+区间最大子段和。
但是如何维护区间最大子段和呢?DP?DP的复杂度是\(O(N)\)的,显然不行,而且,DP也不支持动态的修改。
那么我们当然要选择区间问题的好帮手:区间树了(线段树的别名)。
当然,你得知道线段树是什么,如果不会请走这边:
在鄙人的理解中,线段树作为一种数据结构,其思想是分治的树形DP。就拿最简单的线段树来讲,动态修改+维护区间最大值。我们可以把线段树的每个节点理解成树形DP的状态:表示当前节点维护的区间\([L,R]\)的最大值,那么显而易见,上层节点可以由它的两个儿子推出,符合无后效性和最优子结构。那么这就是一个树形DP嘛!
那为什么又有分治思想呢?因为当前节点维护的区间是二分的,这就是一种分而治之的思想。
那么,除了线段树支持的基本三大操作:求和、求最大/最小值之外,其他的区间维护问题就也可以使用线段树解决。运用的还是分治+树形DP的思想。即只需要考虑这么一个问题:如何设置状态才能使得线段树父子节点之间可以上推。
回到这个问题。动态修改+区间最大子段和。最大子段和怎么维护呀?首先,我想到就粗暴地设置状态为:”\(tree[pos]\)表示本区间的最大子段和。“但是显然的是,这种状态是不支持上推的,它的正确性无法保证,因为可能会有最大子段和是跨过这个区间的中点的,所以不能以中点分成两个儿子来推。
怎么办呢?我们需要合理地设置一个或多个状态,使得这些状态的父子关系可以平滑向上更新。
那么我们考虑这么设:
设置:
\(tree[pos]\)表示当前节点表示区间的最大子段和
\(lmax[pos]\)表示当前节点表示区间一定包含左端点的最大子段和
\(rmax[pos]\)表示当前节点表示区间一定包含右端点的最大子段和
\(sum[pos]\)表示当前节点表示区间的总和
为什么这么设置呢?仍然回到上面讲到的问题:为什么不能只设\(tree[pos]\)表示答案呢?因为可能存在最大子段和的区间横跨中点的情况出现。那么如何才能把这种情况也包含进去呢?简单啊,把这个区间拆开就好了,于是就有了以上\(lmax,rmax\)的状态。
于是,我们可以平滑的转移:
设\(lson/rson\)为当前节点\(pos\)的左右儿子,有:
\(tree[pos]=\max(tree[lson],tree[rson],lmax[rson]+rmax[lson])\)
然后我们还需要考虑如何维护\(lmax[],rmax[]\),也很简单:
\(lmax[pos]=\max(sum[lson]+lmax[rson],lmax[lson])\)
\(rmax[pos]=\max(rmax[rson],rmax[lson]+sum[rson])\)
问题解决:
代码:
#include<cstdio>
#include<algorithm>
#define lson pos<<1
#define rson pos<<1|1
using namespace std;
const int maxn=5*1e5+10;
int n,m;
int w[maxn];
struct node
{
int tree,lmax,rmax,sum;
}a[maxn<<2];
void pushup(int pos)
{
a[pos].sum=a[lson].sum+a[rson].sum;
a[pos].lmax=max(a[lson].lmax,a[lson].sum+a[rson].lmax);
a[pos].rmax=max(a[rson].rmax,a[rson].sum+a[lson].rmax);
a[pos].tree=max(max(a[lson].tree,a[rson].tree),a[lson].rmax+a[rson].lmax);
}
void build(int pos,int l,int r)
{
int mid=(l+r)>>1;
if(l==r)
{
a[pos].tree=a[pos].lmax=a[pos].rmax=a[pos].sum=w[l];
return;
}
build(lson,l,mid);
build(rson,mid+1,r);
pushup(pos);
}
void update(int pos,int l,int r,int x,int k)
{
int mid=(l+r)>>1;
if(l==r)
{
a[pos].tree=a[pos].lmax=a[pos].rmax=a[pos].sum=k;
return;
}
if(x<=mid)
update(lson,l,mid,x,k);
else
update(rson,mid+1,r,x,k);
pushup(pos);
}
node query(int pos,int l,int r,int x,int y)
{
node ret;
int mid=(l+r)>>1;
if(x<=l && r<=y)
return a[pos];
else if(y<=mid)
ret=query(lson,l,mid,x,y);
else if(x>mid)
ret=query(rson,mid+1,r,x,y);
else
{
node p=query(lson,l,mid,x,y);
node q=query(rson,mid+1,r,x,y);
ret.sum=p.sum+q.sum;
ret.lmax=max(p.lmax,p.sum+q.lmax);
ret.rmax=max(q.rmax,q.sum+p.rmax);
ret.tree=max(max(p.tree,q.tree),p.rmax+q.lmax);
}
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
build(1,1,n);
while(m--)
{
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
if(k==1)
{
if(x>y)
swap(x,y);
printf("%d\n",query(1,1,n,x,y).tree);
}
else
update(1,1,n,x,y);
}
return 0;
}