BZOJ2002. [Hnoi2010]Bounce 弹飞绵羊

题目大意

给定一个长度为 n 的序列,第i个位置的绵羊会被弹到 i+ki 这个位置,若不存在 i+ki 这个位置,那么这只绵羊就被弹飞。
给定 m 个操作,询问从某一位置弹飞一只绵羊需要的弹射次数或者修改某一个位置的k值。

Data Constraint
n200000,m100000

题解

显然是一个树模型,绵羊被弹到的点就可以看为当前点的父亲。那么修改操作就可以由LCT的Link和Cut实现,询问就是查询当前点到根的路径长度。

时间复杂度: O(mlog2n)

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 200000 + 10

bool Rev[N] ;
int Size[N] , Pre[N] , Son[N][2] ;
int fa[N] , D[N] ;
int n , m ;

bool IsRoot( int x ) { return Son[Pre[x]][0] != x && Son[Pre[x]][1] != x ; }

void Push( int x ) {
    if ( !Rev[x] ) return ;
    swap( Son[x][0] , Son[x][1] ) ;
    Rev[Son[x][0]] ^= 1 ;
    Rev[Son[x][1]] ^= 1 ;
    Rev[x] = 0 ;
}

void Update( int x ) {
    Size[x] = Size[Son[x][0]] + Size[Son[x][1]] + 1 ;
}

void Rotate( int x ) {
    int F = Pre[x] , G = Pre[F] ;
    int Side = ( Son[F][1] == x ) ;
    if ( !IsRoot(F) ) Son[G][Son[G][1] == F] = x ;
    Pre[x] = G , Pre[F] = x ;
    Son[F][Side] = Son[x][!Side] ;
    Pre[Son[x][!Side]] = F ;
    Son[x][!Side] = F ;
    Update(F) ;
    Update(x) ;
}

void Splay( int x ) {
    D[D[0] = 1] = x ;
    for (int now = x ; !IsRoot(now) ; now = Pre[now] ) D[++D[0]] = Pre[now] ;
    for (int i = D[0] ; i >= 1 ; i -- ) Push(D[i]) ;
    while ( !IsRoot(x) ) {
        int y = Pre[x] , z = Pre[y] ;
        if ( !IsRoot(y) ) {
            if ( ( Son[y][0] == x ) ^ ( Son[z][0] == y ) ) Rotate(x) ;
            else Rotate(y) ;
        }
        Rotate(x) ;
    }
    Update(x) ;
}

void Access( int x ) {
    int last = 0 ;
    while ( x ) {
        Splay(x) ;
        Son[x][1] = last ;
        last = x ;
        x = Pre[x] ;
    }
}

void MakeRoot( int x ) {
    Access(x) ;
    Splay(x) ;
    Rev[x] ^= 1 ;
}

void Link( int u , int v ) {
    MakeRoot(v) ;
    Pre[v] = u ;
}

void Cut( int u , int v ) {
    MakeRoot(u) ;
    Access(v) ;
    Splay(v) ;
    Son[v][0] = Pre[u] = 0 ;
}

void Query( int x ) {
    MakeRoot(n+1) ;
    Access(x) ;
    Splay(x) ;
    printf( "%d\n" , Size[x] - 1 ) ;
}

int main() {
    scanf( "%d" , &n ) ;
    for (int i = 1 ; i <= n ; i ++ ) {
        int k ;
        scanf( "%d" , &k ) ;
        if ( i + k > n ) Pre[i] = fa[i] = n + 1 ;
        else Pre[i] = fa[i] = i + k ;
    }
    scanf( "%d" , &m ) ;
    for (int i = 1 ; i <= m ; i ++ ) {
        int op ;
        scanf( "%d" , &op ) ;
        if ( op == 1 ) {
            int x ;
            scanf( "%d" , &x ) ;
            Query( x + 1 ) ;
        } else {
            int x , k ;
            scanf( "%d%d" , &x , &k ) ;
            x ++ ;
            Cut( x , fa[x] ) ;
            if ( x + k > n ) fa[x] = n + 1 ;
            else fa[x] = x + k ;
            Link( x , fa[x] ) ;
        }
    }
    return 0 ;
}

以上.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值