题目
点这里看题目。
分析
不难想到,这个问题实际上可以按照深度来划分(根的深度为 0),于是这就是一个阶梯 Nim 的问题。
阶梯 Nim 相当经典,它的模型是:
有 (n) 堆石子,编号为 (1sim n),第 (i) 堆有 (a_i) 个石子( (a_i>0) )。每一轮玩家可以选择任意非空的一堆 (k) ,并且可以移动任意多且不为零的石子至 (k-1) 堆(移动第 1 堆的石子相当于直接丢掉它们)。请问先手是否必胜?
问题的结论是:
阶梯 Nim 等价于对奇数堆的石子做 Nim 游戏。
考虑这样的过程:
- 如果对手移动奇数堆的石子,我们就按照 Nim 的策略来移动。这样每次操作后,奇数堆的石子会移动到偶数堆里面,奇数堆就是正常的 Nim 过程。
- 如果对手不按常理出牌,移动了偶数堆(第 (2n) 堆)的 (k) 个石子。那么我们就可以在 (2n-1) 堆里面,移动恰好 (k) 个石子至第 (2n-2) 堆。最后,要么这 (k) 个石子被丢掉,要么 (k) 个石子移动到偶数堆里,这样奇数堆的石子照样不会被影响。
这就是阶梯 Nim 的解决方案。实际上,这个结论是告诉我们,阶梯 Nim 的 SG 和是各个奇数位置的堆的异或和。
回到原题。我们认为根的深度是 0 ,看做是 "垃圾桶" (也就是石子丢弃的位置)。对于同一深度上石子堆,移动它们的过程就相当于 Nim 的过程,于是某一深度的 SG 值就是该层上的石子的异或和;对于不同的深度,我们可以将它们看成是阶梯 Nim 的问题,于是整个游戏的 SG 值就是奇数深度的 SG 值的异或值。
代码:
#include <cstdio>
const int MAXN = 1e5 + 5;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
struct Edge
{
int to, nxt;
}Graph[MAXN << 1];
int dep[MAXN];
int head[MAXN], num[MAXN];
int N, cnt, Nim;
void AddEdge( const int from, const int to )
{
Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
head[from] = cnt;
}
void DFS( const int u, const int fa )
{
if( ( dep[u] = dep[fa] + 1 ) % 2 == 0 ) Nim ^= num[u];
for( int i = head[u] ; i ; i = Graph[i].nxt )
DFS( Graph[i].to, u );
}
void Clean()
{
Nim = cnt = 0;
for( int i = 0 ; i <= N ; i ++ )
head[i] = dep[i] = num[i] = 0;
}
int main()
{
int T;
read( T );
while( T -- )
{
read( N ), Clean();
for( int i = 1, fa ; i < N ; i ++ ) read( fa ), AddEdge( fa, i );
for( int i = 0 ; i < N ; i ++ ) read( num[i] );
DFS( 0, 0 );
puts( Nim ? "win" : "lose" );
}
return 0;
}