spojGSS2 1557 Can you answer these queries II(成段更新)

题意:给你N个数,每个数a[i]的范围在[-100000,100000],然后有Q个查询,每个查询问区间[x,y]内最大子区间和为多少,并且重复出现的值只能计算一次(如果是负数,输出0,即可以子区间可以为空)。

        离线处理查询。每个值只能计算一次的方法同之前的hdu 3333 Turing Tree & hdu 3874 Necklace (成段更新)及Codeforces Round #136 (Div. 2) D. Little Elephant and Array方法一样,不再赘述——解题报告Here

        首先,因为是离线处理,我们是从左到右处理每个a[i]的,这题线段树的每个叶子结点里存储的是,位置1,2,...,i-1,i到当前位置i的区间和sum,及最大子区间和mx。明白了这点,那么问题是如何去维护最大子区间和mx。方法是通过sum,及两个懒操作值,flag_sum和flag_mx(分别表示在更新过程中这个区间的左右子儿子区间需要加上的值,设这个值为valu,并且从flag_sum=0加到flag_sum=valu的过程中,最大值是多少——flag_mx。

        其次,mx,sum,flag_mx,flag_sum的初值都应该是0(因为,子区间可以为空,即应该排除负数)。向下传递两个懒操作值的方法是:子区间的flag_mx值为,其原值、子区间的flag_sum加上当前区间的flag_mx,中取一个较大值。之后,子区间的flag_sum加上当前区间的flag_sum。

        为什么这么做呢?子区间的flag_mx的最大值,除了原值,还有可能出现在,子区间的flag_sum加到子区间的flag_sum加上当前区间的flag_sum的过程中,而这个过程中的最大值显然是子区间的flag_sum加上当前区间的flag_mx。

        最后如何更新mx和sum呢?

        对于非叶子结点:

                当前区间的sum,从子区间的sum值中取较大值,再加上当前区间的flag_sum。

                当前区间的mx,1.从子区间的mx值中取较大值。2.两个子区间的sum中取较大值,

再加上当前区间的flag_mx。

        对于叶子结点:

                当前区间的sum值即是flag_sum,mx即是flag_mx。

        为什么这么做呢?

        起先想能不能用直接用当前区间变化后的flag_sum、flag_mx去更新当前区间的sum、mx。后来发现有一个问题——如果这样做,那么每次变化都要向下传递这两个懒操作值。复杂度就退化成O(N)的了,相当于懒操作值没起作用。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;

#define LL(x) (x<<1)
#define RR(x) (x<<1|1)
#define MID(a,b) (a+((b-a)>>1))
typedef long long LL;
const int N=100005;

struct OP
{
	int st,ed,id;
	OP(){}
	OP(int a,int b,int c){st=a;ed=b;id=c;}
	bool operator<(const OP &b)const
	{ return ed<b.ed; }
}op[N];
struct node
{
	int lft,rht;
	LL mx,sum;
	LL flag_mx,flag_sum;
	int mid(){return MID(lft,rht);}
	void init() { mx=sum=flag_mx=flag_sum=0; }
	void fun(LL a,LL b)
	{
		flag_mx=max(flag_mx,flag_sum+b);
		flag_sum+=a;
	}
};

int n,m,pos[N*2],a[N];
LL res[N];

struct Segtree
{
	node tree[N*4];
	void PushUp(int ind)
	{
		if(tree[ind].lft!=tree[ind].rht)
		{
			tree[ind].sum=max(tree[LL(ind)].sum,tree[RR(ind)].sum)+tree[ind].flag_sum;
			tree[ind].mx=max(tree[LL(ind)].mx,tree[RR(ind)].mx);
			tree[ind].mx=max(tree[ind].mx,
					max(tree[LL(ind)].sum,tree[RR(ind)].sum)+tree[ind].flag_mx);
		}
		else
		{
			tree[ind].sum=tree[ind].flag_sum;
			tree[ind].mx=tree[ind].flag_mx;
		}
	}
	void PushDown(int ind)
	{
		LL &tmp1=tree[ind].flag_sum,&tmp2=tree[ind].flag_mx;
		tree[LL(ind)].fun(tmp1,tmp2); PushUp(LL(ind));
		tree[RR(ind)].fun(tmp1,tmp2); PushUp(RR(ind));
		tmp1=tmp2=0;
	}
	void build(int lft,int rht,int ind)
	{
		tree[ind].lft=lft;	tree[ind].rht=rht;
		tree[ind].init();
		if(lft!=rht)
		{
			int mid=tree[ind].mid();
			build(lft,mid,LL(ind));
			build(mid+1,rht,RR(ind));
		}
	}
	void updata(int st,int ed,int ind,int valu)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed)
		{
			tree[ind].fun(valu,valu);
			PushUp(ind);
		}
		else 
		{
			PushDown(ind);
			int mid=tree[ind].mid();
			if(st<=mid) updata(st,ed,LL(ind),valu);
			if(ed >mid) updata(st,ed,RR(ind),valu);
			PushUp(ind);
		}
	}
	LL query(int st,int ed,int ind)
	{
		int lft=tree[ind].lft,rht=tree[ind].rht;
		if(st<=lft&&rht<=ed) return tree[ind].mx;
		else 
		{
			PushDown(ind);
			int mid=tree[ind].mid();
			LL mx1=0,mx2=0;
			if(st<=mid) mx1=query(st,ed,LL(ind));
			if(ed> mid) mx2=query(st,ed,RR(ind));
			//PushUp(ind);		//查询未更新值,这里可以不用向上传递
			return max(mx1,mx2);
		}
	}
}seg;
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		memset(pos,0,sizeof(pos));
		for(int i=1;i<=n;i++)  scanf("%d",&a[i]);
		scanf("%d",&m);
		for(int i=0;i<m;i++)
		{
			int st,ed;
			scanf("%d%d",&st,&ed);
			op[i]=OP(st,ed,i);
		}
		sort(op,op+m);

		int ind=0;
		seg.build(1,n,1);
		for(int i=1;i<=n;i++)
		{
			int &tmp=pos[a[i]+N];
			seg.updata(tmp+1,i,1,a[i]);
			tmp=i;
			while(ind<m&&op[ind].ed==i)
			{
				res[op[ind].id]=seg.query(op[ind].st,op[ind].ed,1);
				ind++;
			}
		}
		for(int i=0;i<m;i++) printf("%lld\n",res[i]);
	}
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值