Wannafly挑战赛10-D-小H的询问(线段树区间合并)

题目描述:

小H给你一个数组{a},要求支持以下两种操作:

1.  0 l r(1<=l<=r<=n),询问区间[l,r]中权值和最大的有效子区间的权值和,一个子区间被认为是有效的当且仅当这个子区间中没有两个相邻的偶数或者奇数。

2.  1 x v(1<=x<=n,-109<=v<=109),将a[x]的值修改为v。

输入描述:

第一行读入两个正整数n,m(1<=n,m<=105)
第二行读入n个整数,第i个表示a[i](-109 <= a[i] <= 109)

接下来m行,每行三个数表示操作,描述见题目描述。

输出描述:

输出每个询问的答案。

输入:

10 10
-9 -8 -8 -8 2 -7 -5 2 2 3
0 3 5
0 4 4 
0 2 4
1 6 6
1 1 6
1 5 9
0 1 2 
1 5 -8
0 2 4

1 3 -2

输出:

2
-8
-8
6

-8

题解:又有更新又有查询,呢肯定是线段树了,因为每次查询的最大字段和必须要在合法区间,因此普通的区间查询肯定不行,我们考虑区间合并查询,对于当前小区间,无非就是左段合法区间的最大值或者右段合法区间的最大值,当两个区间合成一个大区间的话,答案无非有三种,假如合并出的中间出现新的合法区间,呢最大值在原有的基础上更新即可,定义4个变量即可。具体见代码。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define maxn 100005
#define ll long long
int val[maxn],n,m;
struct node
{
	int l,r;
	ll lm,rm,mm,s;
}sum[maxn<<2];
node merge(node a,node b)
{
	node c;
	c.lm=a.lm;c.rm=b.rm;
	c.mm=max(a.mm,b.mm);c.s=-1e18;
	if((val[a.r]+val[b.l])%2)
	{
		c.mm=max(a.rm+b.lm,c.mm);
		c.lm=max(a.lm,a.s+b.lm);
		c.rm=max(b.rm,b.s+a.rm);
		c.s=max((ll)-1e18,a.s+b.s);
	}
	c.l=a.l;c.r=b.r;
	return c;
}
void build(int id,int l,int r)
{
	if(l==r)
	{
		sum[id].l=sum[id].r=l;
		sum[id].lm=sum[id].rm=sum[id].mm=sum[id].s=val[l];
		return;
	}
	int mid=(l+r)/2;
	build(id*2,l,mid);
	build(id*2+1,mid+1,r);
	sum[id]=merge(sum[id*2],sum[id*2+1]);
}
void update(int id,int l,int r,int k,int v)
{
	if(l==r)
	{
		sum[id].lm=sum[id].rm=sum[id].mm=sum[id].s=v;
		return;
	}
	int mid=(l+r)/2;
	if(k<=mid)
		update(id*2,l,mid,k,v);
	else
		update(id*2+1,mid+1,r,k,v);
	sum[id]=merge(sum[id*2],sum[id*2+1]);
}
node query(int id,int l,int r,int L,int R)
{
	if(L<=l && R>=r)
		return sum[id];
	int mid=(l+r)/2;
	if(R<=mid)
		return query(id*2,l,mid,L,R);
	if(L>mid)
		return query(id*2+1,mid+1,r,L,R);
	return merge(query(id*2,l,mid,L,R),query(id*2+1,mid+1,r,L,R));
}
int main(void)
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&val[i]);
	build(1,1,n);
	while(m--)
	{
		int t,l,r;
		scanf("%d%d%d",&t,&l,&r);
		if(t==1)
		{
			val[l]=r;
			update(1,1,n,l,r);
		}
		else
			printf("%lld\n",query(1,1,n,l,r).mm);
	}
	return 0;
}
/*
10 10
-9 -8 -8 -8 2 -7 -5 2 2 3
0 3 5
0 4 4 
0 2 4
1 6 6
1 1 6
1 5 9
0 1 2 
1 5 -8
0 2 4
1 3 -2
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值