|
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308
/**//* 题意: 给出一个长度为N(N <= 100000)的数列,然后是两种操作: U A B: 将第A个数替换为B (下标从零开始) Q A B: 输出区间[A, B]的最长连续递增子序列 注意:操作的数目m <= 100000。
解法: 线段树
思路: 做惯了区间最值、求和、修改、染色的等问题,这题算比较新颖 的了,由这题可以看出线段树的一般解法,因为这题要求保存的信息 比较多,每个线段树的结点要求保存的信息有以下几个:
int lMax; // 包含结点左端点的最长连续递增子序列的长度 int rMax; // 包含结点右端点的最长连续递增子序列的长度 int Max; // 当前结点的最长连续递增子序列的长度 int lVal, rVal; // 当前结点管辖的区间左右端点的数值 int l, r; // 当前结点管辖的区间
我们用以下函数从左右儿子中得到当前结点的信息: void UpdateBy(Tree* ls, Tree* rs); 之所以把它写成函数是因为这里的处理比较麻烦,很容易出错,并且需要 调用多次,这个函数的作用就是通过左右儿子的信息填充本身的信息。
lVal、rVal、l、r等信息比较容易求得。 lMax和rMax的值就比较麻烦了,需要分情况讨论(下面的len表示区间长度): 1. 左儿子最右边的值 < 右儿子最左边的值
lMax = (左儿子的lMax == 左儿子的len) ? 左儿子的len + 右儿子的lMax : 左儿子的lMax; rMax = (右儿子的rMax == 右儿子的len) ? 右儿子的len + 左儿子的rMax : 右儿子的rMax; Max = MAX(左儿子的rMax + 右儿子的lMax, 左儿子的Max, 右儿子的Max, lMax, rMax);
2. 左儿子最右边的值 >= 右儿子最左边的值
lMax = 左儿子的lMax; rMax = 右儿子的rMax; Max = MAX(左儿子的Max, 右儿子的Max);
一开始读入的时候有一连串数字,需要建树,建树的时候每次回归时需要 将儿子的信息传递给父亲,调用UpdateBy(Tree* ls, Tree* rs)函数,每 次插入完毕后回归时,信息会更新,也需要调用。询问时,返回的也是一 个线段树结点,并且需要将答案合并,还是需要调用UpdateBy函数,所以 总的来说需要调用三次,把它写成一个函数还是势在必行的。 */
#include <iostream>
using namespace std;
#define maxn 100010
struct Tree { int lMax; // 包含结点左端点的最长连续递增子序列 int rMax; // 包含结点右端点的最长连续递增子序列 int Max; // 当前结点的最长连续递增子序列 int lVal, rVal; // 当前区间左右端点的值 int l, r; // 当前结点管辖的区间 int son[2];
void clear() { son[0] = son[1] = -1; } void UpdateBy(Tree* ls, Tree* rs); void Unit(int nl, int nr, int nv); int len() { return r - l + 1; } }T[maxn*4]; int root, tot; int val[ maxn ];
int MAX(int a, int b) { return a > b ? a : b; }
int MAX(int a, int b, int c) { return MAX(MAX(a, b), c); }
int MAX(int a, int b, int c, int d) { return MAX( MAX(a, b), MAX(c, d) ); }
void Tree::UpdateBy(Tree* ls, Tree* rs) { lVal = ls->lVal; rVal = rs->rVal; l = ls->l; r = rs->r; if(ls->rVal < rs->lVal) { lMax = (ls->lMax == ls->len()) ? ls->len() + rs->lMax : ls->lMax; rMax = (rs->rMax == rs->len()) ? rs->len() + ls->rMax : rs->rMax; Max = MAX(ls->rMax + rs->lMax, ls->Max, rs->Max); Max = MAX(Max, lMax, rMax);
}else { lMax = ls->lMax; rMax = rs->rMax; Max = MAX(ls->Max, rs->Max); } }
void Tree::Unit(int nl, int nr, int nv) { lMax = rMax = 1; Max = 1; lVal = rVal = nv; l = nl; r = nr; }
int GetID(int& root) { if(root == -1) { root = tot++; T[root].clear(); } return root; }
void Build(int& root, int l, int r) { GetID(root); if(l == r) { T[root].Unit(l, r, val[l]); return ; } int mid = (l + r) >> 1; Build(T[root].son[0], l, mid); Build(T[root].son[1], mid+1, r);
T[root].UpdateBy(&T[ T[root].son[0] ], &T[ T[root].son[1] ]); }
void Insert(int root, int nPos, int val) { if(nPos < T[root].l || nPos > T[root].r) return ; if(T[root].l == nPos && nPos == T[root].r) { T[root].Unit(nPos, nPos, val); return ; } Insert(T[root].son[0], nPos, val); Insert(T[root].son[1], nPos, val);
T[root].UpdateBy(&T[ T[root].son[0] ], &T[ T[root].son[1] ]); }
Tree Query(int root, int nl, int nr) { if(nl > T[root].r || nr < T[root].l) { Tree tmp; tmp.Max = -1; return tmp; }
if(nl <= T[root].l && T[root].r <= nr) { return T[root]; } Tree A, B; A = Query(T[root].son[0], nl, nr); B = Query(T[root].son[1], nl, nr); if(A.Max == -1) return B; else if(B.Max == -1) return A; else { Tree X; X.UpdateBy(&A, &B); return X; } }
int n, m; int main() { int t, i; scanf("%d", &t);
while(t--) { scanf("%d %d", &n, &m); for(i = 1; i <= n; i++) { scanf("%d", &val[i]); } tot = 0; root = -1; Build(root, 1, n); while(m--) { char str[10]; int A, B; scanf("%s %d %d", str, &A, &B); if(!strcmp(str, "U")) { Insert(root, A+1, B); }else { Tree tmp = Query(root, A+1, B+1); printf("%d\n", tmp.Max); } } } return 0; }
|