HDU 3308 LCIS 线段树 区间合并 入门题
题意:
给出一个长度为N(N <= 100000)的数列,然后是两种操作:
U A B: 将第A个数替换为B (下标从零开始)
Q A B: 输出区间[A, B]的最长连续递增子序列
询问的次数m <= 100000。
类型:区间合并
这题跟POJ hotel差不多,这题应该比hotel简单一些,因为 update是单点更新,所以每次更新数据都是更新到低的,所以不用写pushdown函数,
pushup里面比hotel多了一个判断,即num[m] < num[m]时左右儿子的数据才能合并,否则不合并,其它跟hotel无差。
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define maxn 100004 #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define mid int m = (l + r)>>1 int num[maxn]; int lsum[maxn<<2], rsum[maxn<<2], msum[maxn<<2]; int n, m; void pushup(int l, int r, int rt) { lsum[rt] = lsum[rt<<1]; rsum[rt] = rsum[rt<<1|1]; mid; msum[rt] = max( msum[rt<<1], msum[rt<<1|1]); if(num[m] < num[m+1]) { if(lsum[rt] == m - l + 1) lsum[rt] += lsum[rt<<1|1]; if(rsum[rt] == r - m) rsum[rt] += rsum[rt<<1]; msum[rt] = max( msum[rt], rsum[rt<<1] + lsum[rt<<1|1] ); } } void build(int l, int r, int rt) { if(l == r) { lsum[rt] = rsum[rt] = msum[rt] = 1; return; } mid; build(lson); build(rson); pushup(l, r, rt); } void update(int p, int v, int l, int r, int rt) { if(l == r) { num[p] = v; return; } mid; if(p <= m) update(p, v, lson); else update(p, v, rson); pushup(l, r, rt); } int query(int a, int b, int l, int r, int rt) { if(a <= l && r <= b) return msum[rt]; mid; int ret = 0; if( a <= m ) ret = max( ret, query(a, b, lson) ); if( b > m ) ret = max( ret, query(a, b, rson) ); if(num[m] < num[m+1]) //注意:取最大值时要保证是在给定的区间[a,b]里面 ret = max( ret, min( m - a + 1, rsum[rt<<1]) + min(b - m, lsum[rt<<1|1]) ); return ret; } char op[3]; int main() { int cas, i, j; int a, b; scanf("%d", &cas); while( cas-- ) { scanf("%d%d", &n, &m); for(i = 1; i <= n; i++) scanf("%d", &num[i]); build(1, n, 1); while( m-- ) { scanf("%s%d%d", op, &a, &b); if(op[0] == 'Q') printf("%d\n", query(a+1, b+1, 1, n, 1) );//注意题目中给的位置是从0开始的 else update(a+1, b, 1, n, 1); } } return 0; }


浙公网安备 33010602011771号