5507. 【清华冬令营2018模拟】取石子

题目


正解

直接说做法了,挺好理解的。
钦定 a ≤ b a\le b ab
r = x m o d    ( a + b ) r=x\mod (a+b) r=xmod(a+b)
分四种情况讨论:

  1. r ∈ [ 0 , a − 1 ] r\in [0,a-1] r[0,a1]。这个情况没有意义。
  2. r ∈ [ a , b − 1 ] r\in [a,b-1] r[a,b1]。这个情况下, A A A能多走一步而 B B B不能,所以只要出现了这个情况 A A A必胜。
  3. r ∈ [ b , 2 a − 1 ] r\in [b,2a-1] r[b,2a1]。这个情况下,两个人都能各走一步。所以这个情况相当于改变先后手顺序。
  4. r ∈ [ 2 a , a + b − 1 ] r\in [2a,a+b-1] r[2a,a+b1]。这个情况下, A A A能多走至少 2 2 2步, B B B能多走至少 1 1 1步。如果 B B B先手,相当于有个情况 3 3 3;如果 A A A先手, A A A必胜。如果这个情况出现了两次,那么 A A A无论先手后手都必胜。

可能 b b b 2 a 2a 2a的大小关系会有问题,但是没有太大关系。(按照代码中的那样分类就是了)

答案先乘上 2 c n t 1 2^{cnt1} 2cnt1
计算先手必胜和后手必胜的方案:
先手必胜: ∑ i m o d    2 = 1 ( c n t 3 i ) + ∑ i m o d    2 = 0 ( c n t 3 i ) c n t 4 \sum_{i\mod 2=1} \binom{cnt3}{i}+\sum_{i\mod 2=0}\binom{cnt3}{i}cnt4 imod2=1(icnt3)+imod2=0(icnt3)cnt4
后手必胜: ∑ i m o d    2 = 0 ( c n t 3 i ) \sum_{i\mod 2=0} \binom{cnt3}{i} imod2=0(icnt3)(这里没有 c n t 4 cnt4 cnt4的原因是如果有 A A A必胜)


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define mo 1000000007
#define ll long long
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
ll fac[N],ifac[N];
ll C(int m,int n){return fac[m]*ifac[n]%mo*ifac[m-n]%mo;}
void initC(int n){
	fac[0]=1;
	for (int i=1;i<=n;++i)
		fac[i]=fac[i-1]*i%mo;
	ifac[n]=qpow(fac[n]);
	for (int i=n-1;i>=0;--i)
		ifac[i]=ifac[i+1]*(i+1)%mo;
}
int n,a,b;
int x[N];
ll ansA,ansB,ansF,ansS;
void work(){
	initC(n);
	ll cnt1=0,cnt2=0,cnt3=0,cnt4=0;
	for (int i=1;i<=n;++i){
		int r=x[i]%(a+b);
		if (r<a)
			cnt1++;
		else if (r<b)
			cnt2++;
		else if (r<a*2)
			cnt3++;
		else
			cnt4++;
	}
	for (int i=0;i<=cnt3;++i)
		if (i&1)
			(ansF+=C(cnt3,i))%=mo;
		else{
			(ansS+=C(cnt3,i))%=mo;
			(ansF+=C(cnt3,i)*cnt4)%=mo;
		}
	ll tmp=qpow(2,cnt1);
	(ansF*=tmp)%=mo;
	(ansS*=tmp)%=mo;
	ansA=(qpow(2,n)-ansF-ansS+mo+mo)%mo;
}
int main(){
//	freopen("in.txt","r",stdin);
	freopen("stone.in","r",stdin);
	freopen("stone.out","w",stdout);
	scanf("%d%d%d",&n,&a,&b);
	for (int i=1;i<=n;++i)
		scanf("%d",&x[i]);
	if (a>b){
		swap(a,b);
		work();
		swap(ansA,ansB);
	}
	else
		work();
	printf("%lld %lld %lld %lld\n",ansA,ansB,ansF,ansS);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值