题目链接:hdu3308
题目大意:大致就是给一个带修改动态的序列,让你求每次询问的区间最长上升子序列的长度,这个上升子序列是连续的。
思路:由于是连续的,并且是区间问题可以考虑用线段树去维护它。如何维护这个线段树呢?
我们发现一个区间的LCIS得到的贡献只有三种情况:左区间、右区间、左右区间。如果维护左右区间的LCIS的长度的话到时只要取一个最大的上来即可。如何维护上升和左右区间呢?*
可以多一个区间的前缀最大LCIS和后缀的最大LCIS,在修改的时候父节点可以通过断掉的端点去修改当前的LCIS,因为比如端点如果连续,当前的LCIS会等于左区间后缀+右区间前缀,其他情况同理,详见代码
Code:
#include <stdio.h>
#include <string.h>
#define MAXN 100005
struct Node {
int l, r;
int suf, pre, lcis;
};
Node LTree[MAXN<<2];
int n, m, val[MAXN];
inline int max(int a, int b) {
return a > b ? a : b;
}
void PushUp(int idx) {
int mid = LTree[idx << 1].r;
if (val[mid] < val[mid+1]) {
LTree[idx].pre = LTree[idx << 1].pre;
if (LTree[idx << 1].pre == LTree[idx << 1].r - LTree[idx << 1].l + 1)
LTree[idx].pre += LTree[idx << 1 | 1].pre;
LTree[idx].suf = LTree[idx << 1 | 1].suf;
if (LTree[idx << 1 | 1].suf == LTree[idx << 1 | 1].r - LTree[idx << 1 | 1].l + 1)
LTree[idx].suf += LTree[idx << 1].suf;
LTree[idx].lcis = max(max(LTree[idx << 1].lcis, LTree[idx << 1 | 1].lcis), LTree[idx << 1].suf + LTree[idx << 1 | 1].pre);
} else {
LTree[idx].pre = LTree[idx << 1].pre;
LTree[idx].suf = LTree[idx << 1 | 1].suf;
LTree[idx].lcis = max(LTree[idx << 1].lcis, LTree[idx << 1 | 1].lcis);
}
}
void build(int l, int r, int idx) {
LTree[idx].l = l;
LTree[idx].r = r;
if (l == r) {
LTree[idx].suf = LTree[idx].pre = LTree[idx].lcis = 1;
return ;
}
int mid = l + (r - l >> 1);
build(l, mid, idx << 1);
build(mid+1, r, idx << 1 | 1);
PushUp(idx);
}
void update(int k, int num, int idx) {
if (LTree[idx].l == k && LTree[idx].r == k) {
val[k] = num;
return ;
}
int mid = LTree[idx].l + (LTree[idx].r - LTree[idx].l >> 1);
if (k > mid) update(k, num, idx << 1 | 1);
else update(k, num, idx << 1);
PushUp(idx);
}
int query_pre(int l, int r, int idx) {
if (l <= LTree[idx].l && r >= LTree[idx].r) {
return LTree[idx].pre;
}
int mid = LTree[idx].l + (LTree[idx].r - LTree[idx].l >> 1);
if (r <= mid)
return query_pre(l, r, idx << 1);
else if (l > mid)
return query_pre(l, r, idx << 1 | 1);
else {
int pre = query_pre(l, mid, idx << 1);
if (val[mid] < val[mid+1] && pre == mid - l + 1) pre += query_pre(mid+1, r, idx << 1 | 1);
return pre;
}
}
int query_suf(int l, int r, int idx) {
if (l <= LTree[idx].l && r >= LTree[idx].r) {
return LTree[idx].suf;
}
int mid = LTree[idx].l + (LTree[idx].r - LTree[idx].l >> 1);
if (r <= mid)
return query_suf(l, r, idx << 1);
else if (l > mid)
return query_suf(l, r, idx << 1 | 1);
else {
int suf = query_suf(mid+1, r, idx << 1 | 1);
if (val[mid] < val[mid+1] && suf == r - mid) suf += query_suf(l, mid, idx << 1);
return suf;
}
}
int query(int l, int r, int idx) {
if (l <= LTree[idx].l && r >= LTree[idx].r) {
return LTree[idx].lcis;
}
int mid = LTree[idx].l + (LTree[idx].r - LTree[idx].l >> 1);
if (r <= mid)
return query(l, r, idx << 1);
else if (l > mid)
return query(l, r, idx << 1 | 1);
else {
int rv = query(l, mid, idx << 1);
int lv = query(mid+1, r, idx << 1 | 1);
int mv = 0;
if (val[mid] < val[mid+1]) {
// mv = LTree[idx << 1].suf + LTree[idx << 1 | 1].pre; 易错,查询的[l, r]区间不一定就是左右子树的全部
mv = query_suf(l, mid, idx << 1) + query_pre(mid+1, r, idx << 1 | 1);
}
return max(max(rv, lv), mv);
}
}
int main() {
char cmd[20];
int u, v, i, T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
memset(val, 0, sizeof(val));
for (i = 1; i <= n; i++) scanf("%d", &val[i]);
build(1, n, 1);
for (i = 1; i <= m; i++) {
getchar();
scanf("%s %d %d", &cmd, &u, &v);
if (cmd[0] == 'Q') {
printf("%d\n", query(u+1, v+1, 1));
} else if (cmd[0] == 'U') {
update(u+1, v, 1);
}
}
}
return 0;
}
查询右左子树的前后缀改成这个效率可变成O(1),即查询的效率变成O(logn):mv = min(LTree[idx << 1].suf, mid - l + 1) + min(LTree[idx << 1 | 1].pre, r - mid);