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 2≤n≤106 。
前言
我从未遇到过如此强大的结论。
思路
为什么这样翻译题面,因为这和 O n e I n D a r k \rm OneInDark OneInDark 做过的 一道题 有点类似,反正我没做过。
初看题面,完全没有直接做的办法。本来博弈的DP就很复杂,还要计算方案数。
然而这里有一个结论: n ≥ 2 n≥2 n≥2 时,后手必胜。
为什么?考虑最终不能操作的时候,一定是 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)! (n−k)! 种下法。
我们枚举最终状态,然后算上贡献即可。具体地,先拆环为链,设 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)=(kn−k+1) 。然后分最后一个位置填与不填两种情况讨论,把它接成环,可得答案为 F ( n − 1 , k ) + F ( n − 3 , k − 1 ) F(n-1,k)+F(n-3,k-1) F(n−1,k)+F(n−3,k−1) 。
注意最后答案要乘以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;
}