您的位置:首页 > 编程语言 > C语言/C++

51nod 1169 石子游戏

2017-06-23 21:59 267 查看
1169 石子游戏

题目来源: Interviewstreet

基准时间限制:1 秒 空间限制:131072 KB 分值: 320 难度:7级算法题


 收藏


 关注

有N堆石子(N <= 100),每堆数量不超过10^9,A B两个人玩Nim游戏。Nim游戏的规则如下:A B两个人轮流拿石子,A先拿。每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。在A B开始游戏之前,你可以先做些手脚,从任意多堆取任意多石子使得A必败。但至少有一堆石子的数量不变。问有多少种不同的改变的方法?由于方案数量巨大,输出Mod 10^9
+ 7的结果。

Input
第1行:1个数N(2 <= N <= 100)。
第2 - N + 1行:对应石子的数量C[i](1 <= C[i] <= 10^9)


Output
输出方案数量Mod 10^9 + 7的结果


Input示例
3
246
638
355


Output示例
602


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

数位DP+思路~

题目要求至少有一堆没有取,那么先不管这个限制DP一遍,再把所有堆都取出一个,再不管限制DP一遍,前面的减去后面的就是答案,所以这个问题转化为无限制的。

我们枚举从位k开始改变,(k位一定改变),用f[i][j]表示第i个数,目前i位是0,DP即可。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long

const int mod=1e9+7;

int n,a[102],mi[30],f[102][2],ans;

void add(int &a,int b)
{
a=a+b>=mod ? a+b-mod:a+b;
}

int cal()
{
int ans=0,kkz,now;
for(int k=29;~k;k--)
{
memset(f,0,sizeof(f));f[0][0]=1;
for(int i=1;i<=n;i++)
if(a[i]&mi[k])
{
f[i][0]=(ll)f[i-1][0]*mi[k]%mod+(ll)f[i-1][1]*(a[i]%mi[k]+1)%mod;
f[i][1]=(ll)f[i-1][1]*mi[k]%mod+(ll)f[i-1][0]*(a[i]%mi[k]+1)%mod;
}
else
{
f[i][0]=(ll)f[i-1][0]*(a[i]%mi[k]+1)%mod;
f[i][1]=(ll)f[i-1][1]*(a[i]%mi[k]+1)%mod;
}
kkz=0;now=1;
for(int i=n;i;i--)
{
if(a[i]&mi[k]) add(ans,(ll)f[i-1][kkz]*now%mod);
kkz^=(a[i]&mi[k])>0;now=(ll)now*(a[i]%mi[k]+1)%mod;
}
if(kkz)
{
ans++;break;
}
}
return ans-1;
}

int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);mi[0]=1;
for(int i=1;i<=29;i++) mi[i]=mi[i-1]<<1;
ans=cal();
for(int i=1;i<=n;i++) a[i]--;
ans=(ans-cal()+mod)%mod;
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 思路 数位DP