Games
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^3T≤5,n≤103,d≤10,ai≤103
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
题意:
两个人玩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
****************************************************/