Magician HDU - 5316(线段树 , 单点更新+区间合并)

Magician

题目链接:HDU - 5316

题意:n个数,由1~n编号,每个数的范围是[-1000000000, 1000000000],两种操作:0  a  b :输出区间[a, b]中子序列的最大和,并且该子序列中的每两个相邻元素在原序列中的下标的奇偶性不同;  1   a  b :将下标为a的数改为b;

思路:这里的子序列并不需要连续;对于每段区间都有四种子序列:奇首奇尾(oo), 奇首偶尾(oe), 偶首奇尾(eo), 偶首偶尾(ee),(ood表示奇数, even表示偶数);所以在合并两个相邻区间时就有8种合并方式:

oo+eo=oo, oo+ee=oe, oe+oe=oe, oe+oo=oo, eo+eo=eo, eo+ee=ee, ee+oe=ee, ee+oo=eo;

线段树单点更新再加上上边的区间合并就是updata()这个函数了;

那么查询呢?也说对于在两个相邻区间的查询来说,不能单纯的query(m<<1, l, mid)+query(m<<1|1, mid+1, r),这样合并出来的区间可能并不符合题意;所以我们可以先返回一个区间t,然后像上边一样合并两个相邻区间;

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
struct node{
	int l, r;
	ll oo, oe, eo, ee;
}tr[maxn<<2];
void pushup(int m){
	tr[m].oo=max(max(tr[m<<1].oo, tr[m<<1|1].oo), max(tr[m<<1].oo+tr[m<<1|1].eo, tr[m<<1].oe+tr[m<<1|1].oo));
	tr[m].oe=max(max(tr[m<<1].oe, tr[m<<1|1].oe), max(tr[m<<1].oo+tr[m<<1|1].ee, tr[m<<1].oe+tr[m<<1|1].oe));
	tr[m].eo=max(max(tr[m<<1].eo, tr[m<<1|1].eo), max(tr[m<<1].eo+tr[m<<1|1].eo, tr[m<<1].ee+tr[m<<1|1].oo));
	tr[m].ee=max(max(tr[m<<1].ee, tr[m<<1|1].ee), max(tr[m<<1].eo+tr[m<<1|1].ee, tr[m<<1].ee+tr[m<<1|1].oe));
}
void build(int m, int l, int r){
	tr[m].l=l;
	tr[m].r=r;
	if(l==r){
		ll x;
		scanf("%lld", &x);
		if(l%2){
			tr[m].oo=x;
			tr[m].oe=tr[m].eo=tr[m].ee=-inf;
		}
		else{
			tr[m].ee=x;
			tr[m].oe=tr[m].eo=tr[m].oo=-inf;
		}
		return;
	}
	int mid=(l+r)>>1;
	build(m<<1, l, mid);
	build(m<<1|1, mid+1, r);
	pushup(m);
}
void updata(int m, int index, ll val){
	if(tr[m].l==tr[m].r){
		if(index%2){
			tr[m].oo=val;
		}
		else{
			tr[m].ee=val;
		}
		return;
	}
	int mid=(tr[m].l+tr[m].r)>>1;
	if(index<=mid) updata(m<<1, index, val);
	else updata(m<<1|1, index, val);
	pushup(m);
}
node Union(node a, node b){
	node t;
	t.oo=max(max(a.oo, b.oo), max(a.oo+b.eo, a.oe+b.oo));
	t.oe=max(max(a.oe, b.oe), max(a.oe+b.oe, a.oo+b.ee));
	t.eo=max(max(a.eo, b.eo), max(a.eo+b.eo, a.ee+b.oo));
	t.ee=max(max(a.ee, b.ee), max(a.ee+b.oe, a.eo+b.ee));
	return t;
}
node query(int m, int l, int r){
	if(tr[m].l==l&&tr[m].r==r){
		return tr[m];
	}
	int mid=(tr[m].l+tr[m].r)>>1;
	node temp;
	if(r<=mid) temp=query(m<<1, l, r);
	else if(l>mid) temp=query(m<<1|1, l, r);
	else{
		temp=Union(query(m<<1, l, mid), query(m<<1|1, mid+1, r));
	}
	return temp;
}
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		int n, m;
		scanf("%d%d", &n, &m);
		build(1, 1, n);
		int type, a, b;
		while(m--){
			scanf("%d%d%d", &type, &a, &b);
			if(type){
				updata(1, a, (ll)b);
			}
			else{
				node t=query(1, a, b);
				printf("%lld\n", max(max(t.oo, t.oe), max(t.eo, t.ee)));
			}
		}
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值