码迷,mamicode.com
首页 > 其他好文 > 详细

线段树--小白逛公园nkoj1316

时间:2016-07-13 17:24:44      阅读:134      评论:0      收藏:0      [点我收藏+]

标签:

小白逛公园

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;
}


线段树--小白逛公园nkoj1316

标签:

原文地址:http://blog.csdn.net/incincible/article/details/51888547

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!