作业GFOJ471(sg函数)

7 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值