P1253 扶苏的问题-线段树

题目传送门

题目大意:

给一个数组,给定q次操作,每次操作包含以下三种情况:

先输入op,l,r,若op!=3,则输入x.

  • 若 op=1,则接下来有三个整数 𝑙,𝑟,𝑥,表示将区间 [𝑙,𝑟]内的每个数都修改为 𝑥。
  • 若 op=2,则接下来有三个整数 𝑙,𝑟,𝑥,表示将区间 [𝑙,𝑟] 内的每个数都加上 𝑥。
  • 若 op=3,则接下来有两个整数 𝑙,𝑟,表示查询区间 [𝑙,𝑟] 内的最大值。

这问题很简单,只不过写长代码的时候需要稍微注意一下,不要写错了,我就是因为写错了两个字母,导致这题找bug找了两个小时。

首先可以知道,如果给区间先重置为a,后加b,则该区间数全部会变为最后"a+b",也就是之前的标记全部忽略不计,若先加a,后重置b,则全部变成b,优先级毋庸置疑是重置的优先级高,所以下发懒信息的时候需要先下发重置信息,并且将之前累加的懒信息清零。

 如:

void laze(ll i, ll pl, ll pr, ll add, ll rev) {//add是累加信息,rev是重置信息
	if (rev != inf) {
		tagg[i] = rev;
		tag[i] = 0;
		tree[i] = rev;
	}
	if (add != inf) {
		tag[i] += add;
		tree[i] += add;
	}
}

接下来直接看代码,有不清楚的欢迎评论区讨论或者私信我:

#include<bits/stdc++.h>

using namespace std;
using ll = long long;

const ll N = 1e6 + 5;
const ll inf = 1e18;

//tag为累加add懒信息,tagg为rev重置懒信息
ll tree[N << 2], tag[N << 2], tagg[N << 2];
ll a[N];

int lson(int i) {//访问左儿子
	return i << 1;
}
int rson(int i) {//访问右儿子
	return i << 1 | 1;
}

void up(ll i) {//求当前节点最大值
	tree[i] = max(tree[lson(i)], tree[rson(i)]);
}

void laze(ll i, ll pl, ll pr, ll add, ll rev) {
	if (rev != inf) {//优先级rev比较大,必须先判断rev
		tagg[i] = rev;
		tag[i] = 0;//重置后累加懒信息应该清零
		tree[i] = rev;
	}
	if (add != inf) {
		tag[i] += add;
		tree[i] += add;
	}
}

void down(ll i, ll pl, ll pr) {
	if (tag[i] || tagg[i] != inf) {//如果由任意一个懒信息,都要下发
		ll mid = (pl + pr) >> 1;
		laze(lson(i), pl, mid, tag[i], tagg[i]);
		laze(rson(i), mid + 1, pr, tag[i], tagg[i]);
		tag[i] = 0;
		tagg[i] = inf;
	}
}

void build(ll i, ll pl, ll pr) {//建树
	tag[i] = 0;
	tagg[i] = inf;//先初始化懒信息
	if (pl == pr) {
		tree[i] = a[pl];
		return ;
	}
	ll mid = (pl + pr) >> 1;
	build(lson(i), pl, mid);
	build(rson(i), mid + 1, pr);
	up(i);
}

void updata(ll i, ll pl, ll pr, ll L, ll R, ll add, ll rev) {
	if (L <= pl && pr <= R) {
		laze(i, pl, pr, add, rev);
		return ;
	}
	//需要先下发懒信息,使得子树信息正确
	down(i, pl, pr);

	ll mid = (pl + pr) >> 1;
	if (L <= mid) {
		updata(lson(i), pl, mid, L, R, add, rev);
	}
	if (R > mid) {
		updata(rson(i), mid + 1, pr, L, R, add, rev);
	}
	up(i);
}

ll query(ll i, ll pl, ll pr, ll L, ll R) {
	if (L <= pl && pr <= R) {
		return tree[i];
	}
	//同样需要先下发懒信息
	down(i, pl, pr);
	ll mid = (pl + pr) >> 1;
	ll maxx = -inf;
	if (L <= mid) {
		maxx = max(maxx, query(lson(i), pl, mid, L, R));
	}
	if (R > mid) {
		maxx = max(maxx, query(rson(i), mid + 1, pr, L, R));
	}
	return maxx;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int n, q;
	cin >> n >> q;
	for (int i = 1; i <= n; i++)cin >> a[i];
	build(1, 1, n);//建树
	while (q--) {
		ll op, l, r, x;
		cin >> op >> l >> r;
		if (op == 1) {
			cin >> x;
			updata(1, 1, n, l, r, inf, x);//传入add值为inf,方便laze函数特判
		} else if (op == 2) {
			cin >> x;
			updata(1, 1, n, l, r, x, inf);//传入rev值为inf,方便laze函数特判
		} else {
			cout << query(1, 1, n, l, r) << '\n';
		}
	}
	return 0;
}

今天学习到此结束,大家下次再见吖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值