245. 你能回答这些问题吗

题目链接:https://www.acwing.com/problem/content/246/

解题思路:
由于我们想要logn的时间复杂的情况下得到询问的答案,并且还要进行修改,为了不超时所以想到了使用线段树去求最大连续子序列和。

线段树本身是用到了分治的思想,那么如果用线段树,我们需要用道哪些东西才能得到正确的答案呢?
因为是线段树,所以我们每一个序列都需要分成2个子序列。
那么如果我们吧一个序列直接分成2个子序列,如果我们得到了左边子序列的最大值,也得到右边子序列的最大值,那么将子序列全部合起来的最大值就有3种情况:
1.左边子序列的最大值。
2.右边子序列的最大值。
3.中间某部分连接起来的数值

用线段树的话,关键就是我们如何去维护一些信息的道3这个答案;我们不妨这样想,我们给线段树种添加 lmax, rmax, maxx, sum;分别表示在这个区间内 从最左边点开始序列的最大值, 从最右边的点开始序列的最大值, 本序列的最大值, 整个区间之和。 假如我们得到了有这些信息的左右子节点,那么 1. max(左子树的lmax, 左子树的sum + 右子树的lmax) 2. max(右子树的rmax, 右子树的sum + 左子树的rmax) 3. max(左子树的maxx, 右子树的maxx, 左子树的rmax + 右子树的lmax)

AC代码:

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 5e5 + 5;

struct SegmentTree {
	int l, r;
	int lmax, rmax, maxx;
	int sum; 
}t[maxn * 4];

int a[maxn];

int n, m;

inline void build(int p, int l, int r) {
	t[p].l = l, t[p].r = r;
	if(t[p].l == t[p].r) { t[p].lmax = t[p].rmax = t[p].maxx = t[p].sum = a[l]; return; }
	
	int mid = (l + r) / 2;
	build(p * 2, l, mid);
	build(p * 2 + 1, mid + 1, r);
	
	t[p].sum = t[p * 2].sum + t[p * 2 + 1].sum;
	t[p].lmax = max(t[p * 2].lmax, t[p * 2].sum + t[p * 2 + 1].lmax);
	t[p].rmax = max(t[p * 2 + 1].rmax, t[p * 2 + 1].sum + t[p * 2].rmax);
	t[p].maxx = max(max(t[p * 2].maxx, t[p * 2 + 1].maxx), t[p * 2].rmax + t[p * 2 + 1].lmax);
}

inline void change(int p, int x, int y) {
	if(t[p].l == t[p].r) { t[p].lmax = t[p].rmax = t[p].sum = t[p].maxx = y; return; }
	
	int mid = (t[p].l + t[p].r) / 2;
	if(x <= mid) change(p * 2, x, y);
	else change(p * 2 + 1, x, y);
	
	t[p].sum = t[p * 2].sum + t[p * 2 + 1].sum;
	t[p].lmax = max(t[p * 2].lmax, t[p * 2].sum + t[p * 2 + 1].lmax);
	t[p].rmax = max(t[p * 2 + 1].rmax, t[p * 2 + 1].sum + t[p * 2].rmax);
	t[p].maxx = max(max(t[p * 2].maxx, t[p * 2 + 1].maxx), t[p * 2].rmax + t[p * 2 + 1].lmax);
}

inline SegmentTree ask(int p, int l, int r) {
	if(l == t[p].l && r == t[p].r) return t[p];
	
	int mid = (t[p].l + t[p].r) / 2;
	if(mid >= r) 
		return ask(p * 2, l, r);
	else if(mid < l)
		return ask(p * 2 + 1, l, r);
	else {
		SegmentTree a, b, c;
		
		a = ask(p * 2, l, mid);
		b = ask(p * 2 + 1, mid + 1, r);
		
		c.sum = a.sum + b.sum;
		c.lmax = max(a.lmax, a.sum + b.lmax);
		c.rmax = max(b.rmax, b.sum + a.rmax);
		c.maxx = max(max(a.maxx, b.maxx), a.rmax + b.lmax);
		
		return c; 
	}
}


int main(void) {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	
	build(1, 1, n);
	while(m --) {
		int op, x, y; scanf("%d%d%d", &op, &x, &y);
		if(op == 1) {
			if(x > y) swap(x, y);
			printf("%d\n", ask(1, x, y).maxx);
		}
		else change(1, x, y);
	}
	
	return 0;	
} 

总结:对于有关数据结构和图论的题,如果遇到了困难,我个人觉得最好的理解方式就是暴力,即直接画图尝试一番。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值