• 欢迎来到加基森


    前言

    欢迎来到加基森!

    诺格弗格市长要和你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;
    }
    
  • 相关阅读:
    Linux Shell编程入门
    vim 文件在linux不换行,只显示^M解决办法
    服务器高性能程序 磁盘I/O篇
    车牌识别_转自别人的博客
    ubuntu网络简单设置
    C++设计模式(转载)
    结构算法之道
    C++设计模式工厂方法
    二叉树的深度优先遍历、广度优先遍历和非递归遍历
    iptables
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15483190.html
Copyright © 2020-2023  润新知