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

本文介绍了如何使用线段树来处理一个数组中区间最长不降子序列的数量查询问题。解题思路涉及线段树节点的维护,包括最长不降子序列的长度和区间和,并在更新和查询过程中考虑节点间拼接的可能性。代码实现中展示了线段树的构建、更新和查询过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接
在这里插入图片描述

题目大意:

给你一个长度为 n n n 的数组 a a a
每次有两种操作
1   x   y : a x = y 1 \ x \ y: a_x = y 1 x y:ax=y
2   l   r : 询 问 区 间 [ l , r ] 内 有 多 少 对 ( p , q ) , l ≤ p ≤ q ≤ r   满 足 子 序 列 a p ≤ a p + 1 ≤ ⋯ ≤ a q 2 \ l \ r:询问区间 [ l , r ] 内有多少对 (p, q), l \leq p \leq q \leq r \ 满足子序列 a_p \leq a_{p+1} \leq \cdots \leq a_q 2 l r:[l,r](p,q),lpqr apap+1aq

解题思路:

一看这个问题,很容易想到线段树处理
线段树一个节点,维护了节点答案 s u m sum sum ,从左端点开始的最长不降子序列的长度 l e n l lenl lenl ,以右端点为结束的最长不降子序列长度 l e n r lenr lenr ,线段树区间长度 s z sz sz
更新
l e n l lenl lenl : 首先继承左子树的 l e n l lenl lenl ,如果左子树的 l e n l lenl lenl 等于左子树的 s z sz sz ,且左子树的右端点的值不超过右子树的左端点的值,那么 l e n l lenl lenl 再加上右子树的 l e n l lenl lenl (这说明左右子树在中间可以拼起来)
l e n l = t l . l e n l + ( t l . l e n l = = t l . s z   & &   a [ t l . r ] ≤ a [ t r . l ]   ?   t r . l e n l : 0 ) lenl = tl.lenl + (tl.lenl == tl.sz \ \&\&\ a[tl.r] \leq a[tr.l] \ ?\ tr.lenl : 0) lenl=tl.lenl+(tl.lenl==tl.sz && a[tl.r]a[tr.l] ? tr.lenl:0)

l e n r lenr lenr:同上
l e n r = t r . l e n r + ( t r . l e n r = = t r . s z   & &   a [ t l . r ] ≤ a [ t r . l ]   ?   t l . l e n r : 0 ) lenr = tr.lenr + (tr.lenr == tr.sz \ \&\&\ a[tl.r] \leq a[tr.l] \ ? \ tl.lenr : 0) lenr=tr.lenr+(tr.lenr==tr.sz && a[tl.r]a[tr.l] ? tl.lenr:0)

s u m sum sum:
首先 s u m sum sum 等于左子树的 s u m sum sum 与右子树的 s u m sum sum 之和。如果左子树的右端点的值不超过右子树的左端点的值,那么说明左右子树在中间可以拼起来,那么我们还要加上左子树的 l e n r lenr lenr 乘以 右子树的 l e n l lenl lenl
s u m = t l . s u m + t r . s u m + ( a [ t l . r ] ≤ a [ t r . l ]   ?   t l . l e n r ∗ t r . l e n l : 0 ) sum = tl.sum + tr.sum + (a[tl.r] \leq a[tr.l] \ ?\ tl.lenr * tr.lenl : 0) sum=tl.sum+tr.sum+(a[tl.r]a[tr.l] ? tl.lenrtr.lenl:0)

还有一个要注意的点是,我们在 q u e r y query query 的时候,需要返回一个线段树的节点,这是因为答案不仅受到左右节点的 s u m sum sum 的影响,还会受到该节点的其他值的影响。

代码:

#include <bits/stdc++.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, q;
int a[MAXN];
struct Tree{
	int l, r;
	int lenl, lenr;
	ll sum;
	int sz; 
}tree[MAXN << 2];
Tree operator + (Tree x, Tree y){
	Tree ret;
	ret.sz = x.sz + y.sz;
	ret.l = x.l;
	ret.r = y.r;
	ret.sum = x.sum + y.sum + (a[x.r] <= a[y.l] ?  1ll * x.lenr * y.lenl : 0);
	ret.lenl = x.lenl + (x.lenl == x.sz && a[x.r] <= a[y.l] ? y.lenl : 0);
	ret.lenr = y.lenr + (y.lenr == y.sz && a[x.r] <= a[y.l] ? x.lenr : 0);
	return ret;
}
void pushup(int rt){
	tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void build(int l, int r, int rt){
	tree[rt].l = l;
	tree[rt].r = r;
	if(l == r){
		tree[rt].lenl = tree[rt].lenr = 1;
		tree[rt].sum = 1;
		tree[rt].sz = 1;
		return ;
	}
	int m = (l + r) >> 1;
	build(l, m, rt << 1);
	build(m+1, r, rt << 1 | 1);
	pushup(rt);
}
void update(int pos, int x, int rt){
	if(tree[rt].l == tree[rt].r){
		return ;
	}
	int m = (tree[rt].l + tree[rt].r) >> 1;
	if(pos <= m)
		update(pos, x, rt << 1);
	else
		update(pos, x, rt << 1 | 1);
	pushup(rt);
}
Tree query(int L, int R, int rt){
	if(L <= tree[rt].l && tree[rt].r <= R)
		return tree[rt];
	int m = (tree[rt].l + tree[rt].r) >> 1;
	if(L > m){
		return query(L, R, rt << 1 | 1);
	}
	else if(R <= m){
		return query(L, R, rt << 1);
	}
	else{
		return query(L, R, rt << 1) + query(L, R, rt << 1 | 1);
	}
}

int main()
{
    qc;
	cin >> n >> q;
	for (int i = 1; i <= n; ++i){
	    cin >> a[i];
	}
	build(1, n, 1);
	while(q--){
		int t, l, r;
		cin >> t >> l >> r;
		if(t == 1){
			a[l] = r;
			update(l, r, 1);
		}
		else
			cout << query(l, r, 1).sum << "\n";
	}
    return 0;
}

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值