HDU3308 LCIS (线段树区间合并)

题目链接: LCIS

大致题意

给定一个长度为 n n n的序列. 有 m m m次操作:

U a c 把第 a a a个位置的数字修改为 c c c.

Q l r 询问 [ l , r ] [l, r] [l,r]的最长连续严格递增子序列(下文用LCIS代替).

特别的, 题目下标从 0 0 0开始.

解题思路

线段树 (超经典, 在做树上LCIS时过来验了下pushup)

我们可以通过线段树的区间合并来解决本类题目:

考虑当前有两个区间 l e f t , r i g h t left, right left,right. 设合并后的新区间为 r e s res res.

那么新区间的LCIS会有三种情况:
① 左区间的最长LCIS
② 右区间的最长LCIS
③ 左区间以右端点为终点的LCIS + 右区间以左端点为起点的LCIS

我们会发现情况③存在一个限制条件, 即: 左区间右端点处的值 小于 右区间左端点处的值.


因此我们只需要用线段树维护三类值: 当前区间的LCIS, 当前区间左起的LCIS, 当前区间右终的LCIS.

对应代码中为: f m a x , l m a x , r m a x fmax, lmax, rmax fmax,lmax,rmax.


考虑到上文只说了 f m a x fmax fmax如何去维护, 那么考虑 l m a x , r m a x lmax, rmax lmax,rmax如何向上传递信息呢?

设左区间长度为 l l e n llen llen, 右区间长度为 r l e n rlen rlen.

那么对于新区间的 l m a x lmax lmax: 如果满足 l e f t . l m a x = = l l e n left.lmax == llen left.lmax==llen, 表明左侧区间就是一个CIS, 如果此时端点处满足小于的条件, 则我们可以把右区间的 r i g h t . l m a x right.lmax right.lmax加进来.

最终有 r e s . l m a x = l e f t . l m a x + r i g h t . l m a x res.lmax = left.lmax + right.lmax res.lmax=left.lmax+right.lmax

否则, r e s . l m a x = l e f t . l m a x res.lmax = left.lmax res.lmax=left.lmax.


对于 r m a x rmax rmax的信息传递同理.


我们可以开始快乐的开始~~(RE, WA)~~线段树区间合并啦!

➡️树上LCIS点这里⬅️

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10;
int w[N];
struct node {
	int l, r;
	int lmax, rmax, fmax;
}t[N << 2];
void pushup(node& p, const node& l, const node& r) {
	const int llen = l.r - l.l + 1, rlen = r.r - r.l + 1;
	const int LR = w[l.r], RL = w[r.l];
	p.fmax = max({ l.fmax, r.fmax, LR < RL ? l.rmax + r.lmax : 0 });
	p.lmax = l.lmax + (l.lmax == llen and LR < RL ? r.lmax : 0);
	p.rmax = r.rmax + (r.rmax == rlen and LR < RL ? l.rmax : 0);
}
void pushup(int x) { pushup(t[x], t[x << 1], t[x << 1 | 1]); }

void build(int l, int r, int x = 1) {
	t[x] = { l, r, 1, 1, 1 };
	if (l == r) return;
	int mid = l + r >> 1;
	build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
	pushup(x);
}

void modify(int a, int c, int x = 1) {
	if (t[x].l == t[x].r) {
		w[a] = c; //为了跑一遍pushup();
		return;
	}
	int mid = t[x].l + t[x].r >> 1;
	modify(a, c, x << 1 | (a > mid));
	pushup(x);
}

node ask(int l, int r, int x = 1) {
	if (l <= t[x].l and r >= t[x].r) return t[x];
	int mid = t[x].l + t[x].r >> 1;

	if (r <= mid) return ask(l, r, x << 1);
	if (l > mid) return ask(l, r, x << 1 | 1);

	node left = ask(l, r, x << 1), right = ask(l, r, x << 1 | 1);
	node res = { left.l, right.r, 0, 0, 0 };
	pushup(res, left, right);
	return res;
}
int main()
{
	int T; cin >> T;
	while (T--) {
		int n, m; scanf("%d %d", &n, &m);
		rep(i, n) scanf("%d", &w[i]);
		build(1, n);

		rep(i, m) {
			char s[2]; scanf("%s", s);
			if (*s == 'U') {
				int a, c; scanf("%d %d", &a, &c);
				modify(++a, c);
			}
			else {
				int l, r; scanf("%d %d", &l, &r);
				l++, r++;
				printf("%d\n", ask(l, r).fmax);
			}
		}
	}

	return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值