5507. 【清华冬令营2018模拟】取石子
题目描述
有n堆石子,第i堆有xi个。
Alice和Bob轮流取石子(先后手未定),Alice每次从一堆中取走a个,Bob每次从一堆中取走b个,无法操作者输。
不难发现只会有四种情况:Alice必胜;Bob必胜;先手必胜;后手必胜。
你需要选定若干堆石子(共有2^n种方案),Alice和Bob只能在你选出的堆中取,问以上四种情况对应的方案数。
1<=n<=100000,1<=a,b,xi<=10^9
题解
首先一堆个数为s的石子等价于s%(a+b)的石子,可以从0~a+b-1归纳到k(a+b)~(k+1)(a+b)-1
感觉大概是一个人如果选了而另一个人没选,则局面就会改变,由于双方都是绝顶聪明,所以第一个人肯定是朝着利于自己的方向操作,而另一个人必然会阻止他
设a<=b,把石子分成四类:x>=a&x<b,a<=x<2a&x>=b,x>=2a&x>=b,x<a&x<b
第四类对于情况没有影响,在最后的方案乘2^s4
由于x<a+b,所以一个人选了之后另一个人不能再选,且b只能取一次,a可能可以取多次
分类讨论
①选了第一类 或 不选第一类且第三类选了>=2:因为第一类只能由A选,所以A无论先后手都可以留出一步来取胜;第三类选了至少两个的话B就拆不掉,所以A也必胜
②不选一三类:第二类对于AB都一样,所以奇数先手胜偶数后手胜
③不选第一类且第三类选了一个:A先手必胜,A后手B先手时B一定会先拆掉第三类,所以第二类奇数A胜偶数B胜,因此第二类选奇数A必胜偶数先手必胜
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define mod 1000000007
#define ll long long
#define file
using namespace std;
int a[100001],n,A,B,i,j,k,l,s1,s2,s3,s4;
ll ans,ans1,ans2,ans3,ans4;
bool bz;
ll qpower(ll a,int b) {ll ans=1; while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
void swap(int &x,int &y) {int z=x;x=y;y=z;}
int main()
{
freopen("stone.in","r",stdin);
#ifdef file
freopen("stone.out","w",stdout);
#endif
scanf("%d%d%d",&n,&A,&B);
if (A>B) swap(A,B),bz=1;
fo(i,1,n)
{
scanf("%d",&a[i]),a[i]%=(A+B);
if (a[i]<A) ++s4;
else
{
if (a[i]<A*2)
{
if (a[i]<B) ++s1;
else ++s2;
}
else
{
if (a[i]<B) ++s1;
else ++s3;
}
}
}
ans1=(qpower(2,s1)-1)*(qpower(2,s2+s3))%mod+(qpower(2,s3)-1-s3)*qpower(2,s2)%mod;
ans2=0;
if (s2)
ans1+=qpower(2,s2-1)*s3,ans3=qpower(2,s2-1)*(1+s3),ans4=qpower(2,s2-1);
else
ans3=s3,ans4=1;
if (bz) swap(ans1,ans2);
ans1=(ans1+mod)%mod*qpower(2,s4)%mod;
ans2=(ans2+mod)%mod*qpower(2,s4)%mod;
ans3=(ans3+mod)%mod*qpower(2,s4)%mod;
ans4=(ans4+mod)%mod*qpower(2,s4)%mod;
printf("%lld %lld %lld %lld\n",ans1,ans2,ans3,ans4);
fclose(stdin);
fclose(stdout);
return 0;
}