题意:给定n个数,然后对这n个数进行m次操作。一种操作是将第A个数替换成B,另一种操作是询问区间[a,b]是的最长连续递增子序列的长度。
分析:每个节点分别记录包括左端点的LCIS、包括右端点的LCIS和区间上的LCIS。
代码如下:
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 100010; int val[maxn]; int lnum[maxn<<2], rnum[maxn<<2], mnum[maxn<<2]; // 左边最长、右边最长和区间最长 void pushUp(int l, int r, int rt) { lnum[rt] = lnum[rt<<1]; rnum[rt] = rnum[rt<<1|1]; mnum[rt] = max(mnum[rt<<1], mnum[rt<<1|1]); int m = (l + r) >> 1; if (val[m] < val[m+1]) // 这个地方要特别注意 { if (lnum[rt] == m - l + 1) lnum[rt] += lnum[rt<<1|1]; if (rnum[rt] == r - m) rnum[rt] += rnum[rt<<1]; mnum[rt] = max(mnum[rt], rnum[rt<<1] + lnum[rt<<1|1]); } } void build(int l, int r, int rt) { if (l == r) { lnum[rt] = rnum[rt] = mnum[rt] = 1; return ; } int m = (l + r) >> 1; if (l <= m) build(l, m, rt << 1); if (r > m) build(m + 1, r, rt << 1 | 1); pushUp(l, r, rt); return ; } void update(int p, int c, int l, int r, int rt) { if (l == r) { val[l] = c; return ; } int m = (l + r) >> 1; if (p <= m) update(p, c, l, m, rt << 1); else update(p, c, m + 1, r, rt << 1 | 1); pushUp(l, r, rt); return ; } int query(int L, int R, int l, int r, int rt) { if (L == l && R == r) return mnum[rt]; int m = (l + r) >> 1; if (R <= m) return query(L, R, l, m, rt << 1); if (L > m) return query(L, R, m + 1, r, rt << 1 | 1); else { // a为在左孩子节点中查询到的区间[L,m]上的LCIS // b为在有孩子节点中查询到的区间[m+1,R]上的LCIS // c为跨越左右孩子节点的区间[L,R]上的LCIS int a = query(L, m, l, m, rt << 1); int b = query(m + 1, R, m + 1, r, rt << 1 | 1); int c = 0; if (val[m] < val[m+1]) { c += min(rnum[rt<<1], m - L + 1); c += min(lnum[rt<<1|1], R - m); } return max(a, max(b, c)); } } int main() { int t, n, m; scanf("%d", &t); while (t--) { scanf("%d %d", &n, &m); for (int i = 0; i < n; ++i) scanf("%d", &val[i]); build(0, n - 1, 1); char op[2]; int a, b; while (m--) { scanf("%s %d %d", op, &a, &b); if (op[0] == 'Q') printf("%d\n", query(a, b, 0, n - 1, 1)); else update(a, b, 0, n - 1, 1); } } return 0; }