前言
欢迎来到加基森!
诺格弗格市长要和你van♂游戏。
题目
为表示对你的欢迎,诺格弗格市长要和你玩游戏。他们的游戏很原始,也就是从 (n) 堆石子里轮流取石子,每次只能在一堆石子里取正整数数量的石子,取走最后一颗石子的人获胜。
显然这就是经典的 ( m Nim) 游戏,但是诺格弗格市长充分发挥了他的特性:随机!所以你并不知道初始的石子的摆放是怎么样的,你只知道每堆石子数互不相同且范围在 ([1,2^n-1]) 内。
为了表示对你的尊敬,市长决定让你先手,并向你询问有多少种初始的石子摆放使得你能够获胜。
当然市长不会为难你,为了让你能够一口气说出来而不断气,你只需要告诉他答案对 (10^9+7) 取模的值即可。
要是回答不出来不就尴尬了吗。
(1le nle 10^7.)
讲解
考虑 ( m Nim) 游戏先手获胜条件:异或和不为 (0)。不是很好做,转换成总方案减异或和为 (0)。
奇妙的是,这个东西可以递推求解。
令 (dp_i) 表示 (i) 堆石子异或和为 (0) 的方案数,(mi_i=(2^n-1)^{underline{i}}) 显然表示总方案数。
考虑前 (i-1) 堆石子随便取,最后一堆使得总异或和为零,方案数为 (mi_{i-1})。
若前 (i-1) 堆石子异或和为 (0),由于每堆石子数不能为零,需减去这种不合法情况,方案数为 (dp_{i-1})。
若前 (i-2) 堆石子异或和为 (0),由于每堆石子数不能相同,这种情况也不合法,方案数为 ((i-1) imes(2^n-i+1) imes dp_{i-2})。
于是我们可以得到小清新递推式:
[dp_i=mi_{i-1}-dp_{i-1}-(i-1) imes (2^n-i+1) imes dp_{i-2} (i>2)
]
边界为 (dp_1=dp_2=0),可实现 (O(n)) 递推。
代码
史短代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 10000005;
const int MOD = 1e9+7;
int n;
int dp[MAXN],mi[MAXN];
LL Read()
{
LL x = 0,f = 1; char c = getchar();
while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
int qpow(int x,int y)
{
int ret = 1;
while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
return ret;
}
int main()
{
freopen("yui.in","r",stdin);
freopen("yui.out","w",stdout);
n = Read();
mi[1] = qpow(2,n)-1;
for(int i = 2;i <= n;++ i) mi[i] = mi[i-1] * (mi[1]-i+1ll) % MOD;
for(int i = 3;i <= n;++ i) dp[i] = (mi[i-1] - dp[i-1] - dp[i-2] * (mi[1]-i+2ll) % MOD * (i-1)) % MOD;
Put((0ll+mi[n]-dp[n]+MOD+MOD)%MOD,'
');
return 0;
}