标签:
小白逛公园
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; }
标签:
原文地址:http://blog.csdn.net/incincible/article/details/51888547