线段树--小白逛公园nkoj1316

小白逛公园

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;
}
时间: 2024-08-13 17:36:17

线段树--小白逛公园nkoj1316的相关文章

TYVJ1427 小白逛公园

P1427 小白逛公园 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了.    一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯

JDOJ-1260: VIJOS-P1083 小白逛公园

1260: VIJOS-P1083 小白逛公园 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 329  Solved: 94[Submit][Status][Web Board] Description 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了.         一开始,小白就根据公园的风景给每个公园打了分.小新为了省事,每次遛狗的时候

bzoj1756 Vijos1083 小白逛公园

1756: Vijos1083 小白逛公园 Time Limit: 10 Sec Memory Limit: 64 MB Description 小新经常陪小白去公园玩,也就是所谓的遛狗啦-在小新家附近有一条"公园路",路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白

Vijos 1083 小白逛公园(线段树)

线段树,每个结点维护区间内的最大值M,和sum,最大前缀和lm,最大后缀和rm. 若要求区间为[a,b],则答案max(此区间M,左儿子M,右儿子M,左儿子rm+右儿子lm). --------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<

bzoj1756(vijos1083)--小白逛公园--线段树

Description 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯.同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化. 那么,就请你来帮小白

【线段树】bzoj1756 Vijos1083 小白逛公园

我们知道,求一段序列的最大子段和是O(n)的,但是这样是显然会超时的. 我们需要一个数据结构来支持修改和计算的操作,对于这种修改一个而查询区间的问题,考虑使用线段树. 在线段树中,除了左端点,右端点,左儿子指针,右儿子指针之外,新开4个域——max,maxl,maxr,sum,其中sum为该区间的和,max为该区间上的最大子段和,maxl为必须包含左端点的最大子段和,maxr为必须包含右端点的最大子段和. 然后就……可以用线段树来统计了,注意求得的最大子段和中至少包含1个元素,所以出现了样例那样

Vijos 小白逛公园 线段树加DP

描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择**连续**的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯.同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化. 那么,就请你来帮小白选择公园吧

【BZOJ】1756【VIJOS】1083小白逛公园

描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦-在小新家附近有一条"公园路",路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯.同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化. 那么,就请你来帮小白选择公园

P1427 小白逛公园

时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了.    一开始,小白就根据公园的风景给每个公园打了分-.-.小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a.b两个公园)选择连续的一些公园玩.小白当然希望选出的公园的分数总和尽量高咯.同时,由于一些公园的景