http://www.gdfzoj.com/oj/problem/471
Problem Description
给出一颗满二叉树,以及每个树节点上的石头数量
A和B轮流操作,每次选择一个节点,将其上的任意个石头推向它的儿子
不能不推,若选择的节点为叶节点,则被推的石头消失
A先手,要求输出在保证A必胜的前提下,A第一步有多少种走法
多组数据
Input
第一行一个T,表示数据组数
每组数据先给出一个数n,表示二叉树有n层
接下来有n行,第i行有2^(i-1)个数,表示每个树节点上的石头数量
T<=20,n<=15,其余的数<=10000000
Output
对每组数据输出一个数,表示有多少种走法
Sample Input
4
1
1
2
1
0 0
3
1
2 2
4 4 4 4
4
13
19 13
13 0 9 8
5 4 6 11 2 16 18 6
Sample Output
1
0
6
7
部分样例解释:
为方便理解已将每组数据隔开
将节点从上到下,从左到右依次编号
第一组仅有一种可能走法,即把一号点的一个石子下推
第二组A没有必胜策略,故走法为0
第三组的六种走法分别为
1~2:把一号点的石头下推,共两种
3~6:把二或三号点的石头下推一个,共四种
Problem Source
玲珑杯 Round #9
先把树当奇偶层来看,不妨令最后一层为奇层,则所有奇层的sg函数异或值ans为最终A是否可以获胜。
易知sg[n]=n。
所以对于奇层的每一个点的石子数x,都要使其石子数变成k,使得k与除了x之外所有奇层的异或值(即x^ans)相等。
若x=x^ans,则不用处理当前点;
若
x<x
^
ans
,则应该从上往下推石头,方案数+1;
若
x>x
^
ans
,则应该往下推石头,如果当前层为底层,方案数+1,否则+2(两棵子树)。
#include <cstdio>
#include <algorithm>
#define maxn 20
using namespace std;
int tmp,g,T,n,ans,a[maxn][1<<maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
ans=g=0;
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=(1<<i-1);j++)
scanf("%d",&a[i][j]);
for (int i=n;i>0;i-=2)
for (int j=1;j<=(1<<i-1);j++)
ans^=a[i][j];
if (!ans)
{
printf("0\n");
continue;
}
for (int i=n;i>0;i-=2)
for (int j=1;j<=(1<<i-1);j++)
{
tmp=ans^a[i][j];
if (tmp>a[i][j]&&a[i-1][j+1>>1]>=tmp-a[i][j]) g++;
if (tmp<a[i][j]){
g+=2;
if (i==n) g--;
}
}
printf("%d\n",g);
}
return 0;
}