问题描述
dingyeye喜欢和你玩石子游戏。
dingyeye有一棵n个节点的有根树,节点编号为0到n-1,根为0号节点。游戏开始时,第i个节点上有a[i]个石子。两位玩家轮流操作,每次操作玩家可以选择一个节点,并将该节点上的一些石子(个数不能为0)移动到它的父亲节点上去。如果轮到某位玩家时,该玩家没有任何合法的操作可以执行,则判负。
你在游戏中执先手,你想知道当前局面你能否必胜。
输入描述
本题有多组数据,第一行为一个非负整数T,表示数据组数。
对于每组数据,第一行一个整数n,表示节点数目。
接下来一行为n−1个整数fa[1]⋯fa[n−1],分别描述了除根节点外每个点的父亲。方便起见,保证0<=fa[i]< i。
接下来一行为n个非负整数a[0]⋯a[n−1],分别描述了每个点初始的石子数。保证0≤a[i]<134217728。1≤T≤100,1≤n≤100000。
保证n>100的测试点数目不超过77个。
输出描述
对于每组数据,输出一行,若先手必胜则输出”win”,否则输出”lose”(不含引号)。
输入样例
2
2
0
1000 1
4
0 1 0
2 3 3 3
输出样例
win
lose
题解:阶梯博弈,可以考虑把这个问题转化成Nim博弈:我们考虑到可以把奇数阶梯的数抽象出来,那么可以把从奇数阶梯的数看成Nim,将石子移到父节点可以看作“取石子”,如果是从偶数阶梯的取石子到父节点,我们可以“模拟”这次操作将这些石子继续搬运到下一个父节点,也就是跳过了奇数阶层,那么对奇数阶层做一次异或运算即可。
为什么要用奇数阶层而不是偶数阶层呢?第0层石子是不可动的,相当于是你先移动而最后移到0层的操作是你的对手完成的,这时候你会破坏Nim结构而使胜负结果不好判定。
#include<cstdio>
#include<cmath>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<list>
#define pi 4*atan(1)
#define eps 1e-10
#define INF 0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define FF(i,n) for(int i = 0 ; i < n ; i++)
#define F(i,n) for(int i = 1 ; i <= n ; i++)
#define maxn 100000+10
#define mod 200907
#define met(a) memset(a,0,sizeof(a))
typedef long long LL;
//typedef __int64 LL;
using namespace std;
/*void read(int &x){
#define CH getchar()
char ch; x=0;for(ch=CH;ch<'0'||ch>'9';ch=CH);
for(;ch>='0'&&ch<='9';x=x*10+ch-48,ch=CH);
}*/
LL a[maxn];
vector<LL> v[maxn];
LL flag;
void dfs(int x,int deep)
{
if(deep&1)flag^=a[x];
for(int i=0;i<v[x].size();i++)
{
dfs(v[x][i],deep+1);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
LL n;
cin>>n;
for(int i=0;i<=n;i++)
{
v[i].clear();
}
LL x;
for(int i=1;i<=n-1;i++)
{
cin>>x;
v[x].push_back(i);
}
for(int i=0;i<n;i++)
{
cin>>a[i];
}
flag=0;
dfs(0,0);
if(flag)cout<<"win"<<endl;
else cout<<"lose"<<endl;
}
return 0;
}