题目链接: 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;
}