題目鏈接: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);