取石子(stone)
【题目描述】
有 n 堆石子,第 i 堆有 xi 个。
Alice 和 Bob 轮流取石子(先后手未定),Alice 每次从一堆中取走 a 个,Bob
每次从一堆中取走 b 个,无法操作者输。
不难发现只会有四种情况:Alice 必胜;Bob 必胜;先手必胜;后手必胜。
你需要选定若干堆石子(共有 2^n 种方案),Alice 和 Bob 只能在你选出的堆
中取,问以上四种情况对应的方案数。
【输入数据】
第一行三个整数 n,a,b,第二行 n 个整数 x1~xn。
【输出数据】
一行四个整数,分别表示 Alice 必胜、Bob 必胜、先手必胜和后手必胜的方
案数,对 10^9+7 取模。
【样例输入】
2 2 3
2 3
【样例输出】
2 0 1 1
【样例解释】
选定空集时后手必胜,选定{2}时 Alice 必胜,选定{3}时先手必胜,选定{2,3}时 Alice
必胜。
【数据范围】
对于 10%的数据,n,xi<=5。
对于 50%的数据,n<=20。
对于另外 10%的数据,a=b。
对于又另外 20%的数据,a=1。
对于 100%的数据,1<=n<=100000,1<=a,b,xi<=10^9。
首先我们可以把每堆石子的个数mod(a+b)
这是从博弈学的平衡性(我自己取的名)入手的
下面是个人见解,正确性unknown
首先,这样想一场游戏,只有一堆石子
你取了a个,对方紧跟着你在同一堆取了b个,
一轮减少(a+b)个 ,其实无论经过多少轮结果都是一样的。
该输的照样输,该赢的照样赢
那么就把个数mod(a+b)不就好了?
如果有多堆的话,一人突然对另外一堆来操作
这时,另一人会紧跟前一人去同样的堆操作
且在前一人重新对原来的堆操作之前,也不对前一堆操作
这样,其实第一人只是将操作推后了而已,他还是要操作的。
那么,为啥另一人会跟着呢?
因为前一人换堆肯定是因为他不换就会输
后一人就会赢,所以后一人这样就可以将前一人锁定在必输的情形,这是出于最佳策略
就像那个多个游戏的和什么什么的。。。。。
然后对于所有的堆分类讨论即可,
::
stone:
不妨假设a<b。
每堆石子先对a+b取模,然后可以分为4种:
(1) xi<a,没用。
(2) a<=xi<b,只要存在则a必胜。
(3) b<=xi<2a,只和奇偶性有关。
(4) 2a<=xi,存在至少2个则a必胜,存在1个且(3)为偶数则先手必胜,存在1个且(3)为奇数则a必胜,不存在且(3)为奇数则先手必胜,不存在且(3)为偶数则后手必胜。
注意,在n个物品中选出奇数个物品的方案数为2^(n-1)
(或偶数个物品)
看看杨辉三角就知道 了。
另外这个题将无石子归为后手赢,相当于强行定义C(0,0)=1,这不仅让杨辉三角失去了朴素的美感,还让代码挤满了丑恶的三目运算符。
ACcode:
//神之技巧:ΣC(n,k)=2^(n-1) 其中k为都奇数,或都为偶数,可以从杨辉三角上直观的证明
/*
0:无关
1:b>=u>=a 有则a必胜 因为a可以把这堆屯着,b管不着
然后是重要的两点
I:如果2*a>b
那么一堆石子满足b<=u<2*a
双方只要有一个人拿了一次,就会使它废为第0类,成为争夺的重点,有奇数个则先手赢,否则后手赢
一堆石子满足 u>=2*a>b
那么A只要拿一次就可以把它转化为1然后胜利
所以B的唯一机会就是先手(搞掉唯一的一堆),
所以这样的石子有两堆则A必赢
有一堆则B可能赢(把这堆破坏会浪费B的先手条件,看奇偶)。
无则看奇偶
II:如果2*a<=b
那么a<=2*a<=b
无异于1
u>=b时看奇偶
*/
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define mod 1000000007
#define maxn 100005
#define LL long long
using namespace std;
LL n,a,b;
bool rev;
LL pow(int base,int k){
LL ret=1;
for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=ret*base%mod;
return ret;
}
int main(){
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
LL u,v;
scanf("%lld%lld%lld",&n,&a,&b);
if(a>b) swap(a,b),rev=1;
LL cnt[5]={},ans[5]={};
for(int i=1;i<=n;i++){
scanf("%lld",&u);
u%=(a+b);
cnt[(u>=a) + (u>=b) + (u>=b && u>=2*a)]++;
}
LL inc=pow(2,cnt[0]);//最后计算
ans[0]=((pow(2,cnt[1])-1)*pow(2,cnt[2]+cnt[3])%mod+(pow(2,cnt[3])-cnt[3]-1)*pow(2,cnt[2])%mod+cnt[3]*(cnt[2]?pow(2,cnt[2]-1):0)%mod)%mod;
//ans[1]=0;
ans[2]=((cnt[2]?pow(2,cnt[2]-1):0)+(cnt[2]?pow(2,cnt[2]-1):1)*cnt[3]%mod)%mod;
ans[3]=(cnt[2]?pow(2,cnt[2]-1):1);
for(int i=0;i<4;i++)
ans[i]=(ans[i]*inc)%mod,
ans[i]=(ans[i]+mod)%mod;
if(rev) swap(ans[0],ans[1]);
printf("%lld %lld %lld %lld\n",ans[0],ans[1],ans[2],ans[3]);
}