[线段树] HDU 3306

来源:互联网 发布:wow数据库 编辑:程序博客网 时间:2024/06/10 00:39

感觉线段树的递归还好hhh

但被这个连续递增子序列折腾的够呛hhh

每个节点不仅仅要保存当前最长序列的大小,还要保存前缀和后缀最长序列的大小,以达到正确合并

(自己完全想不出来hhhh)

开始看了代码也是一阵懵逼,把树画出来之后好看很多




#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#define lson rt << 1#define rson rt << 1 | 1using namespace std;const int e = 1000000;int num[ e ];int pre[ e << 2 ];     //保存当前区间从最左开始,连续增长序列的大小(前缀)int suf[ e << 2 ];      //保存当前区间从最右开始,连续增长序列的大小(后缀)int mid[ e << 2 ];      //保存当前区间整个,连续增长序列的大小    // 修改pre,mid,suf保证区间内的序列长度void pushup ( int rt, int l, int r ) {        mid[ rt ] = max ( mid[ lson ], mid[ rson ] );  //默认的序列大小为左右两个孩子的序列大小中较大的数        pre[ rt ] = pre[ lson ];                       //根的前缀由左孩子的前缀决定        suf[ rt ] = suf[ rson ];                       //根的后缀由右孩子的后缀决定        int m = ( l + r ) >> 1;        int len = r - l + 1;        if ( num[ m ] < num[ m + 1 ] ) {               //当前根的左右孩子可以连成一条新的连续序列                mid[ rt ] = max ( mid[ rt ], suf[ lson ] + pre[ rson ] );    //左孩子后缀 + 右孩子前缀                if ( pre[ lson ] == len - len / 2 )    //如果左孩子前缀的大小等于左孩子这段区间的大小                        pre[ rt ] += pre[ rson ];      //则根节点的前缀就可以再连上右孩子的前缀                if ( suf[ rson ] == len / 2 )          //右孩子同理                        suf[ rt ] += suf[ lson ];        }}void build ( int rt, int l, int r ) {        if ( l == r ) {                mid[ rt ] = pre[ rt ] = suf[ rt ] = 1;                return;        }        int m = ( l + r ) >> 1;        build ( lson, l, m );        build ( rson, m + 1, r );        pushup ( rt, l, r );}//单节点更新void update ( int rt, int l, int r, int idx, int val ) {        if ( l == r )                if ( l == idx ) {                        num[ idx ] = val;                        pre[ rt ] = mid[ rt ] = suf[ rt ] = 1;                        return;                }        int m = ( l + r ) >> 1;        if ( idx <= m )                update ( lson, l, m, idx, val );        if ( idx > m )                update ( rson, m + 1, r, idx, val );        pushup ( rt, l, r );}// l,r-->now// L,R-->requiredint query ( int rt, int l, int r, int L, int R ) {        //完全在区间里        if ( l >= L && r <= R )                return mid[ rt ];        int m = ( l + r ) >> 1;                //完全在左孩子的区间里        if ( R <= m )                return query ( lson, l, m, L, R );        //完全在右孩子的区间里        if ( L > m )                return query ( rson, m + 1, r, L, R );        //部分在左区间部分在右区间        int res = max ( query ( lson, l, m, L, R ), query ( rson, m + 1, r, L, R ) );          if ( num[ m ] < num[ m + 1 ] ) {                int prex = min ( suf[ lson ], m - L + 1 );   //取左孩子后缀 和 所求区间在左孩子区间里的大小 的 较小值                int suf = min ( pre[ rson ], R - m );                res = max ( res, prex + suf );        }        return res;}int main () {        int T;        scanf ( "%d", &T );        while ( T-- ) {                int n, m;                scanf ( "%d%d", &n, &m );                for ( int i = 1; i <= n; ++i )                        scanf ( "%d", &num[ i ] );                build ( 1, 1, n );                while ( m-- ) {                        char ins[ 5 ];                        int a, b;                        scanf ( "%s%d%d", ins, &a, &b );                        if ( ins[ 0 ] == 'U' )                                update ( 1, 1, n, ++a, b );                        else if ( ins[ 0 ] == 'Q' ) {                                int c = query ( 1, 1, n, ++a, ++b );                                printf ( "%d\n", c );                        }                }        }        return 0;}


原创粉丝点击