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
",ans1,ans2,ans3,ans4);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/13657121.html