[CF1536F]Omkar and Akmar

280 篇文章 1 订阅
27 篇文章 0 订阅

题目

传送门 to CF

题目概要
有一圈石子,均未涂色。两人轮流操作,每次选择一个未上色的石子,将其涂上 A \rm A A B \rm B B,并且满足任何两个相邻的石子都没有涂上相同的颜色。不能操作者输。

如果两个玩家都 o p t i m a l l y \rm optimally optimally 操作(即,有必胜策略的,选择一个必胜策略;没有的就瞎玩),有多少种不同的游戏过程?两个游戏过程不同,当且仅当进行的轮数不同,或者某一轮中涂色的石子不同,或者涂的颜色不同。

数据范围与提示
石子个数 n ≤ 1 0 6 n\le 10^6 n106

思路

很不幸,我们做过几乎原题,虽然我确实有所遗忘(一开始用博弈思路推了一下),但是还是搞出来了。 H M H R ( H a l f    M e m o r i z i n g    H a l f    R e a s o n i n g ) \rm HMHR(Half\;Memorizing\;Half\;Reasoning) HMHR(HalfMemorizingHalfReasoning) 大法好!

我觉得我的考场注释都足够清晰了:

考虑到,游戏结束时,要么是 -A-B- \text{-A-B-} -A-B- 要么是 -A-X-B- \text{-A-X-B-} -A-X-B-,其中 X \rm X X 表示未涂色。(按:考虑任何一个 A \rm A A,它旁边要么是 B \rm B B,要么是 X \rm X X,而 X \rm X X 必须不能涂色游戏才会结束,所以 X \rm X X 另一边就是 B \rm B B 了。)

所以最终 A , B \rm A,B A,B 数量相等(按:因为将 X \rm X X 去掉之后,得到的就是 A B \rm AB AB 相间的一个环),也就是说一共有偶数次操作。那么无论怎么操作后手都会赢!

于是这是一个类似连续段 d p \tt dp dp 的东西吗?(按:做一道题当然会有很多错误的思路嘛。)

不,应该这样:枚举最终局面,那么二人可以轮流选一个位置填。

如果有 k k k 个空洞,那么二人的填法是 ( n − k ) ! (n-k)! (nk)! 个。

而空洞的唯一限制是不能相邻。先破环为链,记为 f ( n , k ) f(n,k) f(n,k),即,长度为 n n n 的序列选出 k k k 个元素,使得两两不相邻。然后求环的方案:如果最后一个位置不是洞,方案是 f ( n − 1 , k ) f(n-1,k) f(n1,k)(除去了最后一个,其他元素自由选,限制与链相同),否则是 f ( n − 3 , k − 1 ) f(n-3,k-1) f(n3,k1)(除去了最后一个和周边两个,其他元素随便选,限制和链相同)。

f f f 的计算方法显然是 ( n − k + 1 k ) {n-k+1\choose k} (knk+1),这是经典结论——利用隔板法,往 k k k 个元素之间填入不选择的元素,再加上两侧,相当于 n − k + 2 n-k+2 nk+2 个元素( + 2 +2 +2 是为了补齐两侧可以不填)填入 k + 1 k+1 k+1 个缝隙,使得每个缝隙都至少有一个元素。

最后乘一个 2 2 2,因为环的颜色可能是 A B A B ⋯ ABAB\cdots ABAB B A B A ⋯ BABA\cdots BABA,二者不同。

代码

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MaxN = 1000005;
const int Mod = 1e9+7;
int inv[MaxN], jc[MaxN];

int f(int n,int k){
	if(n-k+1 < k || k < 0) return 0;
	return 1ll*jc[n-k+1]*inv[k]%Mod*inv[n-2*k+1]%Mod;
}

int main(){
	int n = readint();
	rep(i,(inv[1]=1)+(jc[1]=1),n){
		jc[i] = 1ll*jc[i-1]*i%Mod;
		inv[i] = (0ll+Mod-Mod/i)*inv[Mod%i]%Mod;
	}
	rep(i,(inv[0]=1)+(jc[0]=1),n)
		inv[i] = 1ll*inv[i]*inv[i-1]%Mod;
	int ans = 0;
	for(int k=0; k<=n; ++k){
		if((n-k)&1) continue; // not reasonable
		int t = (f(n-1,k)+f(n-3,k-1))%Mod;
		ans = (ans+1ll*t*jc[n-k])%Mod;
	}
	printf("%d\n",(ans<<1)%Mod);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值