【CF1536F】Omkar and Akmar

题目

题目链接:https://codeforces.com/problemset/problem/1536/F
有一个包含 \(n\) 个格子的环形棋盘,棋盘上的格子编号为 \(1\)\(n\),满足对于任意整数 \(i\in[1,n-1]\),格子 \(i\) 与格子 \(i+1\) 相邻,特别的,格子 \(1\) 与格子 \(n\) 相邻。
小 A 和小 O 在这个棋盘上轮流操作(小 A 先手)。对于每一轮,每一个人需要选择一个空的格子,并在上面放上字母 A 或字母 B,要求放完字母后不能存在两个相邻的格子有着相同的字母放在上面。如果轮到一个人时没有合法操作,那么那个人输。
输出当两位玩家每一轮都采用最优操作玩这个游戏时,游戏结束后不同的局面数对 \(10^9+7\) 取模后的值。
我们认为两个局面不同,仅当游戏轮数不同,或者对于某一轮,放下的字母或是放下字母的格子不同。
我们认为对于某一轮,一个操作是最优操作仅当这一次操作能够最大化该玩家的获胜机会。换句话讲,如果某位玩家目前有必胜策略,那么他操作之后必须仍然有必胜策略,如果某位玩家没有必胜策略,那么他可以进行任意合法操作。
\(1\leq n\leq10^6\)

思路

结论:后手乱走都必胜。
显然最终的局面去掉空格子之后一定是形如 ABABABBABABA 的。如果先手胜利,那么此时一定存在奇数个被填了的格子,那么去掉空格后必然存在相邻的两个字母一样,可以在相邻的字母之间再填上另一个字母。矛盾。
所以这题的最优策略就是两人都乱走。
那么枚举最后结束时填了的字符数 \(i\)\(i\)\(2\) 的倍数),最终局面只有 A 开头和 B 开头两种可能。两人都乱走的方案数是 \(i!\)。此时可以在任意相邻两个字符中插入最多一个空格。
如果第一个字符前不插入空格,方案数为 \(\binom{i}{n-i}\),否则方案数为 \(\binom{i-1}{n-i-1}\)
所以答案就是

\[\sum^{n}_{i=1}[2|i]2\left(\binom{i}{n-i}+\binom{i-1}{n-i-1}\right)i! \]

时间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=1000010,MOD=1e9+7;
int n,fac[N],inv[N];
ll ans;

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

ll C(int n,int m)
{
	if (n<m) return 0;
	return 1LL*fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}

void prework(int n)
{
	fac[0]=inv[0]=1;
	for (int i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%MOD;
	inv[n]=fpow(fac[n],MOD-2);
	for (int i=n-1;i>=1;i--) inv[i]=1LL*inv[i+1]*(i+1)%MOD;
}

int main()
{
	scanf("%d",&n);
	prework(n);
	for (int i=2;i<=n;i+=2)
		ans=(ans+2LL*fac[i]*(C(i,n-i)+C(i-1,n-i-1)))%MOD;
	cout<<ans;
	return 0;
}
posted @ 2021-06-23 11:03  stoorz  阅读(92)  评论(0编辑  收藏  举报