JZOJ4759. 石子游戏

来源:互联网 发布:淘宝买家注册 编辑:程序博客网 时间:2024/06/05 17:05

题目大意

给定一棵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<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std ;#define N 100000 + 10int 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 ;}

以上.

1 0
原创粉丝点击