CF1536F - Omkar and Akmar——博弈结论、组合数学

F - Omkar and Akmar

题目描述

Translated by OneInDark \text{Translated by OneInDark} Translated by OneInDark

有一圈 n n n 颗有标号的石子,均未涂色。两人轮流操作,每次选择一个未上色的石子,将其涂上 或 ,并且满足任何两个相邻的石子都没有涂上相同的颜色。不能操作者输。

如果两个玩家都按最优策略操作(即,有必胜策略的,选择一个必胜策略;没有的就瞎玩),有多少种不同的游戏过程?两个游戏过程不同,当且仅当进行的轮数不同,或者某一轮中涂色的石子不同,或者涂的颜色不同。

数据范围与提示

2 ≤ n ≤ 1 0 6 2\le n\le 10^6 2n106

前言

我从未遇到过如此强大的结论。

思路

为什么这样翻译题面,因为这和 O n e I n D a r k \rm OneInDark OneInDark 做过的 一道题 有点类似,反正我没做过。

初看题面,完全没有直接做的办法。本来博弈的DP就很复杂,还要计算方案数。

然而这里有一个结论: n ≥ 2 n≥2 n2 时,后手必胜。

为什么?考虑最终不能操作的时候,一定是 A B A B A B . . . \rm ABABAB... ABABAB... A B X A B . . . \rm ABXAB... ABXAB... ,X表示未填,而且不会出现相邻两个X。这种情况A和B的数量一定相等,总共填的次数一定是偶数,所以后手必胜。

有了这个结论,这题就简单多了。由于胜负已定,两人就没有什么最优策略可言,只有瞎JB下。所以如果最终有 k k k 个X未填,那么两人有 ( n − k ) ! (n-k)! (nk)! 种下法。

我们枚举最终状态,然后算上贡献即可。具体地,先拆环为链,设 F ( n , k ) F(n,k) F(n,k) 表示链上有 k k k 个X的方案数,由于X不相邻,显然 F ( n , k ) = ( n − k + 1 k ) F(n,k)={n-k+1 \choose k} F(n,k)=(knk+1) 。然后分最后一个位置填与不填两种情况讨论,把它接成环,可得答案为 F ( n − 1 , k ) + F ( n − 3 , k − 1 ) F(n-1,k)+F(n-3,k-1) F(n1,k)+F(n3,k1)

注意最后答案要乘以2,因为 A B A B . . . \rm ABAB... ABAB... B A B A . . . \rm BABA... BABA... 的情况一一对应。

代码

#include<cstdio>//JZM YYDS!!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#define ll long long
#define MAXN 1000005
#define uns unsigned
#define MOD 1000000007ll
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
inline ll ksm(ll a,ll b,ll mo){
	ll res=1;
	for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
	return res;
}
int n;
ll fac[MAXN<<1],inv[MAXN<<1];
inline ll C(int n,int m){
	if(m>n||m<0)return 0;
	return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline ll F(int n,int m){return C(n-m+1,m);}
signed main()
{
	n=read();
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for(int i=2;i<=(n<<1);i++)fac[i]=fac[i-1]*i%MOD;
	inv[n<<1]=ksm(fac[n<<1],MOD-2,MOD);
	for(int i=(n<<1)-1;i>1;i--)inv[i]=inv[i+1]*(1ll+i)%MOD;
	ll ans=0;
	for(int k=0;(k<<1)<=n;k++)
		if(~(n-k)&1)ans=(ans+(F(n-1,k)+F(n-3,k-1))%MOD*fac[n-k]%MOD)%MOD;
	printf("%lld\n",(ans<<1)%MOD);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值