Codeforces Round #742 (Div. 2) E. Non-Decreasing Dilemma

Codeforces Round #742 (Div. 2)的其他题解点这

E. Non-Decreasing Dilemma

原题链接


题目大意:

给出n个数,有m个操作
1 x w 将x位置上的数改成w
2 l r 查询区间[ l , r ]内有多少个连续的递增区间(单个数也算)

思路:

由于是连续的,而且两个区间如果满足条件可以合并成一个新的区间,再次计算答案

这时我们想到线段树的pushup操作
如果我们用线段树来维护连续递增区间的大小,并用sum来存储该区间对答案的贡献,在pushup中通过该区间的左右儿子来计算sum即可
详细解释会放到代码中

AC代码:

#include<cstdio>
#include<iostream>
#include<cstring>
//#define inf 0x7FFFFFFFFFFFFFFF
#define ll long long 
#define ri register int 
using namespace std;
const int maxn = 2e5 + 7;
int a[maxn], b[maxn];
struct num_node {
	int l, r;
	int lm, rm;//分别表示左右儿子的最大连续递增区间的大小
	ll sum;//表示[l,r]区间内有多少个连续递增区间
}s[maxn << 2];
int n, m;
inline void pushup(int k) {
	s[k].sum = s[k << 1].sum + s[k << 1 | 1].sum;
	s[k].lm = s[k << 1].lm, s[k].rm = s[k << 1 | 1].rm;
	if (a[s[k << 1].r] <= a[s[k << 1 | 1].l]) {//如果左右儿子可以合并
		//更新当前节点的lm和rm
		if (s[k << 1].lm == s[k << 1].r - s[k << 1].l + 1) s[k].lm += s[k << 1 | 1].lm;
		if (s[k << 1 | 1].rm == s[k << 1 | 1].r - s[k << 1 | 1].l + 1) s[k].rm += s[k << 1].rm;
		//
		//计算答案
		s[k].sum += 1ll * s[k << 1].rm * s[k << 1 | 1].lm;
		//
	}
}
inline void build(int k, int l, int r) {
	s[k].l = l; s[k].r = r;
	if (l == r) {
		s[k].sum = 1;//一个数,答案为1
		s[k].lm = s[k].rm = 1;
		return;
	}
	int mid = (l + r) >> 1;
	build(k << 1, l, mid);
	build(k << 1 | 1, mid + 1, r);
	pushup(k);
}
inline void update(int k, int x, int w) {
	int l = s[k].l, r = s[k].r;
	if (l == r) {
		a[x] = w;//虽然我们可以直接更新,但是我们需要pushup来更新线段树的节点信息
		return;
	}
	int mid = (l + r) >> 1;
	if (mid >= x)update(k << 1, x, w);
	if (mid < x)update(k << 1 | 1, x, w);
	pushup(k);
}
inline ll query_sum(int k, int L, int R) {
	int l = s[k].l, r = s[k].r;
	if (l > R || r < L) return 0;
	if (L <= l && r <= R) {
		return s[k].sum;
	}
	pushup(k);
	int mid = (l + r) >> 1;
	ll ans = 0;
	if (mid >= L)ans += query_sum(k << 1, L, R);
	if (mid < R)ans += query_sum(k << 1 | 1, L, R);
	if (a[s[k << 1].r] <= a[s[k << 1 | 1].l]) { // 如果可以合并
		int lsum = min(mid - L + 1, s[k << 1].rm), rsum = min(R - (mid + 1) + 1, s[k << 1 | 1].lm);
		if (lsum > 0 && rsum > 0)ans += 1ll * lsum * rsum;
		//防止,lsum,rsum为0
	}
	return ans;
}
int L[maxn], R[maxn];
int main() {
	int n, m;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &a[i]);
	}
	build(1, 1, n);
	while (m--) {
		int opt;
		scanf("%d", &opt);
		if (opt == 1) {
			int x, w;
			scanf("%d %d", &x, &w);
			update(1, x, w);
		}
		else {
			int L, R;
			scanf("%d %d", &L, &R);
			printf("%lld\n", query_sum(1, L, R));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值