51nod 3071 线段树练习2

51nod 3071 线段树练习2

题目

你需要维护一个长为 n 的序列,支持 m 个操作:

  1. 区间修改为一个数
  2. 查询一个区间中的最大子段和,即和最大的子区间的值
数据范围:

对于 30% 的数据:n≤8,m≤10。
对于 70% 的数据:n≤1000,m≤10000。
对于 100% 的数据:1≤n, m≤100000。
保证任意时刻数列中任意元素的和在int范围内。

输入格式:

第一行包含两个整数 n, m,分别表示该数列数字的个数和操作的总个数。
第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。
接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:

  1. 1 l r k:将区间 [l, r] 内每个数都变成 k。
  2. 2 l r:输出区间 [l, r] 内的最大子段和。
输出格式:

输出包含若干行整数,即为所有操作 2 的结果。

输入样例:

5 3
1 -2 3 -4 5
1 1 2 1
2 2 3
2 4 5

输出样例:

4
5

思路

主要分情况讨论。
e.g.左儿子开始的最大子段和,要和左儿子的和加上右儿子从左开始的最大子段和比较。
因为要把值修改成k而不是加k所以简单地修改t[p].sum = len§ * k;

代码

#include <iostream>
#define lp p << 1
#define rp p << 1 | 1
using namespace std;

const int maxn = 1e5 + 5;

int n;
int m;
int a[maxn];

struct segtree {
	int l;
	int r;
	int tag;
	int sum;
	int maxL;
	int maxR;
	int maxn;
	int lazy;
} t[maxn << 2];

int len(int p) {
	return t[p].r - t[p].l + 1;
}
void brush(int p, int k) {
	t[p].maxL = t[p].maxR = t[p].maxn = t[p].sum = len(p) * k;
	t[p].lazy = k;t[p].tag = 1;
}
void update(int p) {//分类讨论
	t[p].sum = t[lp].sum + t[rp].sum;
	t[p].maxL = max(t[lp].maxL, t[lp].sum + t[rp].maxL);
	t[p].maxR = max(t[rp].maxR, t[rp].sum + t[lp].maxR);
	t[p].maxn = max(max(t[lp].maxn, t[rp].maxn), t[lp].maxR + t[rp].maxL);
}
void push_down(int p) {
	brush(lp, t[p].lazy);
	brush(rp, t[p].lazy);
	t[p].lazy = 0;t[p].tag = 0;
}

void build(int p, int l, int r) {
	t[p].l = l;
	t[p].r = r;
	if (l == r) {
		t[p].maxL = t[p].maxR = t[p].sum = t[p].maxn = a[l];
		return;
	}
	int mid = l + r >> 1;
	build(lp, l, mid);
	build(rp, mid + 1, r);
	update(p);
} 

void change(int p, int l, int r, int k) {
	if (t[p].l >= l and t[p].r <= r) {
		brush(p, k);
		return;
	}
	int mid = t[p].l + t[p].r >> 1;
    if (t[p].tag) push_down(p);
	if (l <= mid)
		change(lp, l, r, k);
	if (r > mid)
		change(rp, l, r, k);
	update(p);
}

segtree query(int p, int l, int r) {
	if (t[p].l >= l and t[p].r <= r) {
		return t[p];
	}
	segtree T, f1, f2;
	T.sum = 0;
	if (t[p].tag) push_down(p);
	int mid = t[p].l + t[p].r >> 1;
	if (l > mid) T = query(rp, l, r);
	if (r <= mid) T = query(lp, l, r);
	if (l <= mid and r > mid) {
		f1 = query(lp, l, r);
		f2 = query(rp, l, r);
		T.maxn = max(max(f1.maxn, f2.maxn), f1.maxR + f2.maxL);
		T.maxL = max(f1.maxL, f1.sum + f2.maxL);
		T.maxR = max(f2.maxR, f2.sum + f1.maxR);
		T.sum = f1.sum + f2.sum;
	}
	return T;
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	build(1, 1, n);
	for (int i = 1; i <= m; i++) {
		int op;
		cin >> op;
		if (op == 1) {
			int l, r, k;
			cin >> l >> r >> k;
			change(1, l, r, k);
		}
		else {
			int l, r;
			cin >> l >> r;
			segtree ans = query(1, l, r);
			if (ans.maxn < 0)
				cout << 0 << endl;
			else cout << ans.maxn << endl;
		}			
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值