小白逛公园
Time Limit:20000MS Memory Limit:65536K
Case Time Limit:2000MS
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
Source
vijos
分析:最大连续和
难点:getans里面要求的区间骑在两个儿子的头上,貌似无法直接解决,递归!!!
详情请见代码注释部分:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define inf 1e9
using namespace std;
const int maxn=500005;
int triplemax(int a,int b,int c){
return max(a,max(b,c));
}
struct node{
int a,b;
int l1,r1,mid1,max1;
int sum;
};
node tree[maxn*4];
int a[maxn];
void update(int p){
int l=(p<<1),r=(p<<1)+1;
tree[p].sum=tree[l].sum+tree[r].sum;
tree[p].l1=max(tree[l].l1,tree[l].sum+tree[r].l1);
tree[p].r1=max(tree[r].r1,tree[r].sum+tree[l].r1);
tree[p].max1=triplemax(tree[l].max1,tree[r].max1,tree[l].r1+tree[r].l1);
}
void build_tree(int p,int x,int y){
tree[p].a=x;tree[p].b=y;
if(x<y){
build_tree(p<<1,x,(x+y)>>1);
build_tree((p<<1)+1,((x+y)>>1)+1,y);
update(p);
}
else
tree[p].l1=tree[p].r1=tree[p].mid1=tree[p].max1=tree[p].sum=a[x];
}
void change(int p,int k,int d){
if(tree[p].a>k||tree[p].b<k)return;
if(tree[p].a==tree[p].b){
tree[p].l1=d;
tree[p].r1=d;
tree[p].mid1=d;
tree[p].max1=d;
tree[p].sum=d;
return;
}
if(tree[(p<<1)].a<=k&&k<=tree[(p<<1)].b){
change((p<<1),k,d);
}
if(tree[(p<<1)+1].a<=k&&k<=tree[(p<<1)+1].b){
change((p<<1)+1,k,d);
}
update(p);
}
int getsum(int p,int x,int y){
if(tree[p].b<x||tree[p].a>y)return 0;
if(tree[p].b<=y&&tree[p].a>=x)return tree[p].sum;
else{
int total=0;
total+=getsum((p<<1),x,y);
total+=getsum(((p<<1)+1),x,y);
return total;
}
}
int getr(int p,int l,int r,int t){
//p号区间从右往左不超过t的最大值
int ls=(p<<1),rs=(p<<1)+1,mid=(l+r)>>1;
if(t<=l) return tree[p].r1; //完全包含,直接返回从右开始的最大值
if(mid+1<=t) return getr(rs,mid+1,r,t); //右儿子都没包含完,递归下去
return max(tree[rs].sum+getr(ls,l,mid,t),tree[rs].r1); //在左儿子中间,两种情况:1、右儿子从右往左的最大值 2、右儿子全部+左儿子递归下去
}
int getl(int p,int l,int r,int t){
//p号区间从左往右不超过t的最大值
int ls=(p<<1),rs=(p<<1)+1,mid=(l+r)>>1;
if(r<=t) return tree[p].l1; //完全包含,直接返回从左开始的最大值
if(t<=mid) return getl(ls,l,mid,t); //左儿子都没包含完,递归下去
return max(tree[ls].sum+getl(rs,mid+1,r,t),tree[ls].l1); //在右儿子中间,分两种情况:1、左儿子从左往右的最大值 2、左儿子全部+右儿子递归下去
}
int getans(int p,int x,int y){
if(x>tree[p].b||y<tree[p].a)return -inf;
if(x<=tree[p].a&&tree[p].b<=y)return tree[p].max1;
if(tree[p].a==tree[p].b)return -inf;
int maxl=-inf,maxr=-inf,maxmid=-inf;
if(!(y<tree[p].a||tree[(p<<1)].b<x)) maxl=max(maxl,getans((p<<1),x,y));
if(!(y<tree[(p<<1)+1].a||tree[p].b<x))maxr=max(maxr,getans((p<<1)+1,x,y));
if(x<=tree[(p<<1)].b&&y>=tree[(p<<1)+1].a){
maxmid=max(maxmid,getr((p<<1),tree[(p<<1)].a,tree[(p<<1)].b,x)+getl((p<<1)+1,tree[(p<<1)+1].a,tree[(p<<1)+1].b,y));
}
return triplemax(maxl,maxr,maxmid);
}
int main(){
int n,m,i,j,k;
cin>>n>>m;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
build_tree(1,1,n);
/*cout<<"****************"<<endl;
for(i=1;i<=9;i++){
cout<<i<<"号结点("<<tree[i].a<<","<<tree[i].b<<") l1:"<<tree[i].l1<<" r1:"<<tree[i].r1<<" max1:"<<tree[i].max1<<" sum"<<tree[i].sum<<endl;
}
cout<<"****************"<<endl;*/
for(i=1;i<=m;i++){
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
if(k==1){
if(x>y)swap(x,y);
printf("%d\n",getans(1,x,y));
}
if(k==2){
change(1,x,y);
a[x]=y;
/*cout<<"****************"<<endl;
for(j=1;j<=9;j++){
cout<<j<<"号结点("<<tree[j].a<<","<<tree[j].b<<") l1:"<<tree[j].l1<<" r1:"<<tree[j].r1<<" max1:"<<tree[j].max1<<" sum"<<tree[j].sum<<endl;
}
cout<<"****************"<<endl;*/
}
}
}
Solution 2 by spark:
对于一个区间,维护以下几个值:
Lmax: 包含左端点的最大连续和。
Rmax:包含右端点的最大连续和。
Max:该区间中的最大连续和。
Sum: 该区间的所有元素之和。
每次修改后维护信息即可。
如果把一个区间o 分成左右两个区间ls 、rs,可以得到类似动态规划的方程(想一想为什么):
val[o].Sum = val[ls].Sum+ val[rs].Sum;
val[o].Lmax = max( val[ls].Lmax , val[ls].Sum+val[rs].Lmax); //讨论是否跨过中点
val[o].Rmax = max( val[rs].Rmax , val[rs].Sum+val[ls].Rmax);
val[o].Max =max (val[ls].Max, val[rs].Max, val[rs].Lmax+val[ls].Rmax );
为了便于计算,查询操作需要返回更多的信息,这样可以简化代码。
代码如下:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=500000+5,inf=1e9;
int n,m;
inline void _read(int &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
inline int getmax(int x,int y,int z){
if(x>=z&&x>=y) return x;
if(y>=z) return y; return z;
}
struct node{
int Lmax,Rmax,Max,Sum;
}val[maxn*5];
int s[maxn],sum[maxn],ql,qr,p;
void maintain(int o,int L,int R){ //维护节点的信息
int mid=(L+R)>>1,ls=o*2,rs=o*2+1;
val[o].Sum=val[ls].Sum+ val[rs].Sum;
val[o].Lmax=val[ls].Lmax;
val[o].Rmax=val[rs].Rmax;
val[o].Lmax=max(val[o].Lmax ,+val[ls].Sum+val[rs].Lmax);
val[o].Rmax=max(val[o].Rmax ,val[rs].Sum+val[ls].Rmax);
val[o].Max=getmax(val[ls].Max,val[rs].Max,val[rs].Lmax+val[ls].Rmax);
}
void build(int o,int L,int R){
if(L==R)
val[o].Lmax=val[o].Rmax=val[o].Max=val[o].Sum=s[L];
else {
int mid=(L+R)>>1;
build(o*2,L,mid); build(o*2+1,mid+1,R);
val[o].Sum=sum[R]-sum[L-1];
maintain(o,L,R);
}
}
void update(int o,int L,int R){
if(L==R)
val[o].Lmax=val[o].Rmax=val[o].Max=val[o].Sum=s[L];
else {
int mid=(L+R)>>1;
if(p<=mid) update(o<<1,L,mid);
else update(o*2+1,mid+1,R);
maintain(o,L,R);
}
}
node query(int o,int L,int R){
//返回的是所询问的区间的一系列答案, 包括Lmax,Rmax,Sum,Max
if(ql<=L&&qr>=R) return val[o]; //完全包含
int mid=(L+R)>>1;
if(qr<=mid) return query(o<<1,L,mid); //只在左区间
else if(ql>mid) return query(o*2+1,mid+1,R); //只在右区间
else if(ql<=mid&&qr>mid){ //与左右区间都有交集
int ls=o*2,rs=ls+1;
node l=query(ls,L,mid); //左边
node r=query(rs,mid+1,R); //右边
node ans;
ans.Sum=l.Sum+r.Sum;
ans.Lmax=max(l.Lmax, l.Sum+r.Lmax);
ans.Rmax=max(r.Rmax, r.Sum+l.Rmax);
ans.Max=getmax(l.Rmax+r.Lmax, l.Max, r.Max);
return ans;
}
}
int main(){
int i,k,S;
_read(n); _read(m);
for(i=1;i<=n;i++){
_read(s[i]);
sum[i]=sum[i-1]+s[i];
}
build(1,1,n);
while(m--){
_read(k);
if(k==1){
_read(ql); _read(qr);
if(ql>qr) swap(ql,qr); //注意题目中没有保证ql<=qr
printf("%d\n",query(1,1,n).Max);
}
else {
_read(p) ;_read(S);
s[p]=S;
update(1,1,n);
}
}
return 0;
}