[BZOJ1095]Hide 捉迷藏--括号序列&&线段树

来源:互联网 发布:类似米赚的软件 编辑:程序博客网 时间:2024/05/17 23:01

岛姐 orz

看题解看了接近两个小时才想通这是在干什么
经常看着某一部分就忘了上面一部分要干什么了,然后一脸蒙…
Emmmmm好在最后还是理解到了,十分巧妙

有点晚了,题解有时间再写吧,先贴几个挺不错的题解,一起看可能比较容易理解
—>岛姐的题解
–>博客园-沐阳

自带大常数的代码

/**************************************************************    Problem: 1095    User: Izumihanako    Language: C++    Result: Accepted    Time:2212 ms    Memory:28280 kb****************************************************************//*-----------------------------------------岛姐orz这种解法比较神奇把树上的信息转换成了序列信息然后对序列进行花式维护整个题的重点在于线段树的updata函数通过多维护几个东西,来快速的计算出答案 -----------------------------------------*/#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;int N , Q , head[100005] , tp ;int pos[100005] , cate[300005] , tot , dark[100005] ;struct Path{    int pre , to ;}p[200005] ;struct Node{    int dis , RP , RM , LP , LM , a , b ;    //RP = right_plus , RM = right_minus    //LP = left_plus , LM = left_minus     Node *ls , *rs ;    void set( int x ){        /*a代表的是']' 开口向右的那种括号          b代表的是'[' 开口向左的那种括号          因为把可以匹配的括号消除之后,剩下的一定是这样的:          .....]]]]]][[[[[.....          (a,b)二元组即可表示这个括号         */        /*如果为白点或者括号,就全部赋值为负无限大          这样保证了,凡是有括号是跨过了白点的,通通都取不到         */        a = ( x == -2 ) ;        b = ( x == -1 ) ;        dis = -0x3f3f3f3f ;        if( x >= 1 && dark[x] ){            //如果没有关灯,就设为0,相对于负无穷,这就代表开始了             RP = RM = LP = LM = 0 ;        } else RP = RM = LP = LM = -0x3f3f3f3f ;    }    void updata(){        if( rs->a > ls->b ){            a = ls->a + rs->a - ls->b ; b = rs->b ;        } else {            a = ls->a ; b = ls->b - rs->a + rs->b ;        }        //因为是左右拼接,所以是ls->Rx + rs->Lx才能拼接起中间部分        dis = max( ls->dis , rs->dis ) ;         dis = max( dis , ls->RM + rs->LP ) ;        dis = max( dis , ls->RP + rs->LM ) ;        //因为RP必须与右端联通,所以需要直接 - rs->a + rs->b ,下面同理         RP = max( ls->RP - rs->a + rs->b , ls->RM + rs->a + rs->b ) ;        RP = max( RP , rs->RP ) ;         RM = max( ls->RM + rs->a - rs->b , rs->RM ) ;        //LM维护的是b-a,因此和RM不太一样,是反过来的         LP = max( rs->LP + ls->a - ls->b , rs->LM + ls->a + ls->b ) ;        LP = max( LP , ls->LP ) ;        LM = max( rs->LM + ls->b - ls->a , ls->LM ) ;    }}w[600005] , *root , *tw = w ;void In( int t1 , int t2 ){    p[++tp].pre = head[t1] ;    p[ head[t1] = tp ].to = t2 ;}void dfs( int u , int f ){    /*进入的时候为'['      在这里把点也直接放进序列,更方便处理      (就可以很方便的把白点设置成-inf从而阻断跨过白点的序列)     */    cate[++tot] = -1 ;    pos[ cate[++tot] = u ] = tot ;    for( int i = head[u] ; i ; i = p[i].pre ){        int v = p[i].to ;        if( v != f ) dfs( v , u ) ;    }    //出去的时候为']'     cate[++tot] = -2 ;}Node *build( int lf , int rg ){    //线段树的日常--build     Node *nd = ++tw ;    if( lf == rg )        nd->set( cate[lf] ) ;    else {        int mid = ( lf + rg ) >> 1 ;        nd->ls = build( lf , mid ) ;        nd->rs = build( mid+1 , rg ) ;        nd->updata() ;    } return nd ;}void Modify( Node *nd , int lf , int rg , int pos ){    //线段树的日常--modify     if( lf == rg ){        nd->set( cate[lf] ) ;        return ;    }    int mid = ( lf + rg ) >> 1 ;    if( pos <= mid ) Modify( nd->ls , lf , mid , pos ) ;    else             Modify( nd->rs , mid+1 , rg , pos ) ;    nd->updata() ;}void solve(){    char ss[5] ;    scanf( "%d" , &Q ) ;    for( int i = 1 , x ; i <= Q ; i ++ ){        scanf( "%s" , ss ) ;        switch( ss[0] ){            case 'G':{                //答案会在不断地updata后被统计到根节点                //因此每次询问直接输出根节点的dis即可                 printf( "%d\n" , root->dis ) ;                break;            }            case 'C':{                scanf( "%d" , &x ) ;                //注意这里需要把开关灯状态更新                 dark[x] ^= 1 ;                Modify( root , 1 , tot , pos[x] ) ;                break;            }        }    }}//读入优化 等于scanf int read_(){    int rt = 0;    char ch = getchar() ;    while( ch < '0' || ch > '9' ) ch = getchar() ;    while( ch >='0' && ch <='9' ) rt = (rt<<3) + (rt<<1) + ch - '0' , ch = getchar() ;    return rt;}int main(){    scanf( "%d" , &N ) ;    //一开始灯泡全部处于关闭状态,注意需要将标记打好     for( register int i = 1 ; i <= N ; i ++ ) dark[i] = 1 ;    //日常建边&&一系列预备操作     for( register int i = 1 , t1 , t2 ; i < N ; i ++ ){        t1 = read_() ; t2 = read_();        In( t1 , t2 ) ; In( t2 , t1 ) ;    }    dfs( 1 , 1 ) ;    root = build( 1 , tot ) ;    solve() ;}