sdut 4220 Games(“浪潮杯”山东省第九届ACM大学生程序设计竞赛 G)

Games

Time Limit: 1000 ms  Memory Limit: 65536 KiB
Problem Description

Alice and Bob are playing a stone game. There are nn piles of stones. In each turn, a player can remove some stones from a pile (the number must be positive and not greater than the number of remaining stones in the pile). One player wins if he or she remove the last stone and all piles are empty. Alice plays first.
To make this game even more interesting, they add a new rule: Bob can choose some piles and remove entire of them before the game starts. The number of removed piles is a nonnegative integer, and not greater than a given number dd. Note dd can be greater than nn, and in that case you can remove all of the piles.
Let ansans denote the different ways of removing piles such that Bob are able to win the game if both of the players play optimally. Bob wants you to calculate the remainder of ansans divided by 10^9+7109+7.

Input

The first line contains an integer TT, representing the number of test cases.
For each test cases, the first line are two integers nn and dd, which are described above.
The second line are nn positive integers a_iai, representing the number of stones in each pile.
T \leq 5, n \leq 10^3, d \leq 10, a_i \leq 10^3T5,n103,d10,ai103

Output

For each test case, output one integer (modulo 10^9 + 7109+7) in a single line, representing the number of different ways of removing piles that Bob can ensure his victory.

Sample Input
2
5 2
1 1 2 3 4
6 3
1 2 4 7 1 2 
Sample Output
2
5
Hint
Source
“浪潮杯”山东省第九届ACM大学生程序设计竞赛(感谢山东财经大学)

题意:

两个人玩Nim游戏,但Bob能在游戏开始前完全取走最多d堆石子,问Bob能获胜的方案数。(Alice先手)

通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。

思路:

Nim取石子有一个结论就是把所有数异或起来的值为0的话就是后手赢,否则就是先手赢。那么我们考虑如何在游戏开始前取石子使得异或值为0。

我们枚举他取走j堆石子时Bob赢的方案数,我们可以设:dp[j][k]为Bob取走j堆石子时的Nim游戏的k状态方案数。

K状态的定义:把所有数异或起来的值为k。

我们先求出一个都不取的异或值temp。

令dp[0][temp]=1,其他为0。

有这样一个状态转移方程:dp[j][k]=dp[j][k]+dp[j-1][k^a[i]]

k^a[i]是什么意思呢?因为前面我们已经异或过a[i]的值,我再异或一次就相当于没异或过a[i],即我把第i堆石子取走了。

最后对dp[0~min(n,d)][0]的方案数相加就是答案。

示例程序:

#include <bits/stdc++.h>
#define LDQ 1000000007
using namespace std;
long long dp[11][2001];
int main()
{
    int n,d,a[1000],t,i,i1,i2,temp;
    long long sum;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&d);
        temp=0;
        for(i=0;n>i;i++)
        {
            scanf("%d",&a[i]);
            temp=temp^a[i];
        }
        sum=0;
        memset(dp,0,sizeof(dp));
        dp[0][temp]=1;
        for(i=0;n>i;i++)
        {
            for(i1=min(n,d);i1>=1;i1--)		//题目没说n>=d,所以最多能取min(n,d),并且需要反着推,防止第i堆重复计算
            {
                for(i2=0;2000>=i2;i2++)			//两个值异或最大为两个数相加,a[i]<=1000,所以我取背包容量最大为2000
                {
                    dp[i1][i2]=(dp[i1][i2]+dp[i1-1][i2^a[i]])%LDQ;
                }
            }
        }
        for(i=0;min(n,d)>=i;i++)
        {
            sum=(sum+dp[i][0])%LDQ;
        }
        printf("%lld\n",sum);
    }
    return 0;
}


/***************************************************
Code Len: 859 B
Result: Accepted
Take time: 264ms
Take Memory: 380KB
Submit time: 2018-05-31 21:26:04
****************************************************/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值