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

题目链接

题目大意

给你一个长度为 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 : 2 \ l \ r: 2 l r: 问你区间 [ l , r ] [l, r] [l,r] 内有多少对 ( p , q ) , l ≤ p ≤ q ≤ r (p, q), l \leq p \leq q \leq r (p,q),lpqr 满足子序列 a p ≤ a p + 1 ≤ ⋯ ≤ a q a_p \leq a_{p+1} \leq \cdots \leq a_q 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 l lenl lenl 再加上右子树的 l e n l lenl lenl (这说明左右子树在中间可以拼起来),那么我们还要加上左子树的 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 的影响,还会受到该节点的其他值的影响。

Code

#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
using namespace std;
const int MAXN = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
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);
	}
}
void solve(){
    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";
		}
	}
}

int main()
{
    #ifdef ONLINE_JUDGE
    #else
       freopen("in.txt", "r", stdin);
       freopen("out.txt", "w", stdout);
    #endif

    qc;
    int T;
    //cin >> T;
    T = 1;
    while(T--){

        solve();
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值