JZOJ4759. 石子游戏

题目大意

给定一棵 n 个节点的树和 t 个操作。
一开始每个结点有 ai 个石子。
操作有三种:
操作
对于1操作的询问,两人在以 v 为根节点的子树上玩Nim游戏。每次一人可以选择从除根节点外的一点取出不超过 m 个石子到它的父亲,判断先手是否必胜。

Data Constraint
n,t50000

题解

如果是在序列上做游戏,那么这个游戏就等价于每个结点石子数为 ai%(m+1) 的Nim游戏。
然后放到树上,就类似阶梯Nim游戏了。显然只有在到根距离为奇数的结点上做游戏才是有效的。
得到以上两条结论之后,现在问题就转化为询问子树内深度为奇数或者偶数的结点权值异或和。
因为有增加操作,所以考虑用Splay维护DFS序。
那么怎么找 v 的子树呢?我们记在DFS序上, v 后面第一个深度小于等于 v 的结点为 x 。那么 v 的子树就是 [DFN[v]+1,DFN[x]1] 这一段。我们只要在Splay里维护子树内深度最小的结点就可以查找出 x 了。

SRC

#include
#include
#include
#include
#include
using namespace std ;

#define N 100000 + 10

int Node[2*N] , Next[2*N] , Head[N] , tot ;

int Son[N][2] , Pre[N] , Val[N] , Deep[N] ;
int Size[N] , OddXor[N] , AllXor[N] , MinDep[N] ;
int A[N] , Dep[N] , D[N] ;

int a[N] , dep[N] ;
int n , m , t ;
int Root , Cnt ;

bool Check( int x ) {
    return x != 0 ;
}

void link( int u , int v ) {
    Node[++tot] = v ;
    Next[tot] = Head[u] ;
    Head[u] = tot ;
}

void DFS( int x , int F ) {
    D[++D[0]] = x ;
    for (int p = Head[x] ; p ; p = Next[p] ) {
        if ( Node[p] == F ) continue ;
        dep[Node[p]] = dep[x] + 1 ;
        DFS( Node[p] , x ) ;
    }
}

void Update( int now ) {
    int ls = Son[now][0] ;
    int rs = Son[now][1] ;
    MinDep[now] = min( Deep[now] , min( MinDep[ls] , MinDep[rs] ) ) ;
    OddXor[now] = OddXor[ls] ^ OddXor[rs] ^ ( (Deep[now] & 1) ? Val[now] : 0 ) ;
    AllXor[now] = AllXor[ls] ^ AllXor[rs] ^ Val[now] ;
    Size[now] = Size[ls] + Size[rs] + 1 ;
}

int Build( int l , int r , int Fa ) {
    if ( l > r ) return 0 ;
    int mid = (l + r) / 2 ;
    int now = D[mid] ;
    Pre[now] = Fa ;
    Val[now] = A[mid] ;
    Deep[now] = Dep[mid] ;
    Son[now][0] = Build( l , mid - 1 , now ) ;
    Son[now][1] = Build( mid + 1 , r , now ) ;
    Update( now ) ;
    return now ;
}

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

void Splay( int x , int Goal ) {
    while ( Pre[x] != Goal ) {
        int y = Pre[x] , z = Pre[y] ;
        if ( z != Goal ) {
            if ( (Son[y][0] == x) ^ (Son[z][0] == y) ) Rotate(x) ;
            else Rotate(y) ;
        }
        Rotate(x) ;
    }
    if ( !Goal ) Root = x ;
}

int Find( int x ) {
    if ( MinDep[Son[x][0]] <= Deep[Root] ) return Find(Son[x][0]) ;
    if ( Deep[x] <= Deep[Root] ) return x ;
    return Find(Son[x][1]) ;
}

int Query( int now ) {
    Splay( now , 0 ) ;
    int Rson = Find(Son[Root][1]) ;
    Splay( Rson , Root ) ;
    now = Son[Rson][0] ;
    if ( Deep[Root] & 1 ) return Check( AllXor[now] ^ OddXor[now] ) ;
    else return Check( OddXor[now] ) ;
}

void Modify( int now , int v ) {
    Splay( now , 0 ) ;
    Val[now] = v ;
    Update(now) ;
}

void Insert( int x , int y , int v ) {
    Deep[y] = Deep[x] + 1 ;
    Val[y] = v ;
    Splay( x , 0 ) ;
    Pre[y] = x ;
    Son[y][1] = Son[x][1] ;
    Son[x][1] = y ;
    Pre[Son[y][1]] = y ;
    Update(y) ;
    Update(x) ;
}

int main() {
    scanf( "%d%d" , &n , &m ) ;
    for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &a[i] ) ;
    for (int i = 1 ; i < n ; i ++ ) {
        int u , v ;
        scanf( "%d%d" , &u , &v ) ;
        link( u , v ) ;
        link( v , u ) ;
    }
    scanf( "%d" , &t ) ;
    dep[1] = 1 ;
    DFS( 1 , 0 ) ;
    D[++D[0]] = n + t + 1 ;
    A[n+t+1] = Dep[n+t+1] = 0 ;
    for (int i = 1 ; i <= D[0] ; i ++ ) {
        A[i] = a[D[i]] % (m + 1) ;
        Dep[i] = dep[D[i]] ;
    }
    memset( MinDep , 63 , sizeof(MinDep) ) ;
    Root = Build( 1 , n + 1 , 0 ) ;
    int Last = 0 ;
    for (int i = 1 ; i <= t ; i ++ ) {
        int op ;
        scanf( "%d" , &op ) ;
        if ( op == 1 ) {
            int v ;
            scanf( "%d" , &v ) ;
            v = v ^ Last ;
            if ( Query(v) ) printf( "Yes\n" ) , Last ++ ;
            else printf( "No\n" ) ;
        } else if ( op == 2 ) {
            int x , y ;
            scanf( "%d%d" , &x , &y ) ;
            x = x ^ Last ;
            y = y ^ Last ;
            Modify( x , y % (m + 1) ) ;
        } else {
            int u , v , x ;
            scanf( "%d%d%d" , &u , &v , &x ) ;
            u = u ^ Last ;
            v = v ^ Last ;
            x = x ^ Last ;
            Insert( u , v , x % (m + 1) ) ;
        }
    }
    return 0 ;
}

以上.

你可能感兴趣的:(题解,Splay,Nim游戏)