[SDOI2011]黑白棋

题目描述

小A和小B又想到了一个新的游戏。

这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色。

最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。

小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子。

每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。当谁不可以操作时,谁就失败了。

小A和小B轮流操作,现在小A先移动,有多少种初始棋子的布局会使他胜利呢?

输入输出格式

输入格式:

共一行,三个数,n,k,d。

输出格式:

输出小A胜利的方案总数。答案对1000000007取模。

输入输出样例

输入样例#1:

10 4 2

输出样例#1:

182


题解

组合数学+\(niimK\)游戏

以前没具体学过博弈论==
先扯一下\(nimK\)游戏是个啥东西:

\(nimK\)游戏
\(nimK\)游戏与\(nim\)游戏类似,就是给你n堆石子,最后那个不能取石子的人输,与常规\(nim\)游戏不同的是每个人一次可以从\(K\)堆中取若干棋子

必败态就是对于二进制下的每一位,所有堆石子在二进制下的该位的和能被\((K+1)\)整除(可以类比\(nim\)游戏,或者也可以说成是\(nim2\)游戏\(?\)总之就是我不会证

然后看这个题
好像题面没有给全,就是黑棋只能往左,白棋只能往右,黑白棋子必须相间
所以就可以把这\(k/2\)对左边是黑棋右边是白棋之间的空隙看做是每堆石子
那么就成了\(nimK\)游戏求先手必胜态
先手必胜不好求,考虑先手必败态再用总方案数减去先手必胜态即可
那就设\(f[i][j]\)表示前i位已经满足条件(即所有堆石子的二进制这一位之和能被\(K+1\)整除)
dp即可

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 10005 ;
const int N = 20 ;
const int mod = 1e9 + 7 ;
using namespace std ;

int n , m , k , w ;
int ans , f[N][M] , fac[M] ;

inline int Fpw(int Base , int k) {
    int temp = 1 ;
    while(k) {
        if(k & 1) temp = 1LL * temp * Base % mod ;
        Base = 1LL * Base * Base % mod ; k >>= 1 ;
    }
    return temp ;
}
inline int C(int n , int m) {
    return 1LL * fac[n] * Fpw(fac[m] , mod - 2) % mod * Fpw(fac[n - m] , mod - 2) % mod ;
}
/*
f[i][j] 表示已经考虑了前i位,前i位的异或和都是0了,这样已经花掉了j的空间
你现在有m-1堆,每一位的二进制位必须是k的倍数
你一共能分配的点数是n-m
*/
int main() {
    scanf("%d%d%d",&n,&m,&k) ;
    fac[0] = 1 ; f[0][0] = 1 ; ++ k ;
    for(int i = 1 ; i <= n ; i ++)
        fac[i] = 1LL * fac[i - 1] * i % mod ;
    for(int i = 0 ; i <= 14 ; i ++) {
        for(int j = 0 ; j <= n - m ; j ++) {
            if(!f[i][j]) continue ;
//  现在来枚举这一位上有几个堆去放东西
            for(int t = 0 ; t <= m / 2 ; t += k) {
                if(j + (1 << i) * t > n - m) break ;
                f[i + 1][j + (1 << i) * t] = (f[i + 1][j + (1 << i) * t] + 1LL * f[i][j] * C(m / 2 , t) % mod) % mod ;
            }
        }
    }
    ans = C(n , m) ;
//  把一对黑白点看做一个点
    for(int i = 0 ; i <= n - m ; i ++)
        ans = ((ans - 1LL * f[15][i] * C( n - m / 2 - i , m / 2 ) % mod) % mod + mod) % mod ;
    printf("%d\n",ans) ;
    return 0 ;
}

原文地址:https://www.cnblogs.com/beretty/p/10493650.html

时间: 2024-07-30 18:33:44

[SDOI2011]黑白棋的相关文章

[Sdoi2011]黑白棋 题解

2281: [Sdoi2011]黑白棋 Time Limit: 3 Sec  Memory Limit: 512 MBSubmit: 592  Solved: 362[Submit][Status][Discuss] Description 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同. 小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子. 每

[BZOJ2281][SDOI2011]黑白棋(K-Nim博弈)

2281: [Sdoi2011]黑白棋 Time Limit: 3 Sec  Memory Limit: 512 MBSubmit: 626  Solved: 390[Submit][Status][Discuss] Description 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同. 小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子. 每

【bzoj2281】 Sdoi2011—黑白棋

http://www.lydsy.com/JudgeOnline/problem.php?id=2281 (题目链接) 题意 一个1*n的棋盘,棋盘上一个隔一个的放着个黑棋和白棋,最左端是白棋,最右端是黑棋每次可以向左或向右移动<=d颗棋子,移动不能跨越棋子,也不能越出边界,问先手必胜的初始状态有多少. Solution Knim. 右转介绍:http://blog.csdn.net/weixinding/article/details/7321139 左转题解:http://blog.csdn

bzoj 2281: [Sdoi2011]黑白棋

再次,,,,,虚(一开始看错题了,看成一次移动一个棋子,能移动1-d个格子...这样的话有没有大神会做??本蒟蒻就教) 额,,直接%%%%把...http://hzwer.com/5760.html 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<map> 5 #include<queue> 6 #define N 1000005 7 #define

【bzoj2281】[Sdoi2011]黑白棋

博弈论---Nimk问题. dp再搞搞. 很容易看出,该游戏的终态是每两个棋子都紧靠着.当一颗棋子移动,另一方与该棋子对应的那一刻可以立即追上,使得仍旧紧靠,最终棋子动弹不得,游戏结束. 还能看出,对于白色棋子(先手),往左走没有意义.因为黑子(后手)可以紧随其上使得两者距离不变.同理黑子只往左走.(黄学长貌似提出了反例?) 所以,问题可以抽象为Nim,与传统Nim只能选1堆不同,你可以选1-d堆. 这个拓展问题叫做Nimk问题.对于这种问题,我们可以证明:当将n堆棋子化为二进制,每一位上如果1

游戏开发(三)——WIN32 黑白棋(三)——游戏画面的现实

整个游戏分3部分介绍. 1.棋局的现实 2.AI的现实 3.游戏画面的现实 提供一下完整项目下载 这是第三部分:画面的显示 这部分其实就比较简单的,说白了就是api的堆砌. 主要了解下windows的消息机制,以及怎么画图 主要是分别封装了下对棋盘,棋子,以及当前轮到谁,当前比分是多少,就是游戏画面上不同的部分的绘制. void DrawReversiBoard(); void DrawReversiPieces(EnumReversiPiecesType type, int row_y, in

人机对战-黑白棋

先大致了解一下黑白棋: 规则 如果玩家在棋盘上没有地方可以下子,则该玩家对手可以连下.双方都没有棋子可以下时棋局结束,以棋子数目来计算胜负,棋子多的一方获胜. 在棋盘还没有下满时,如果一方的棋子已经被对方吃光,则棋局也结束.将对手棋子吃光的一方获胜. 翻转棋类似于棋盘游戏"奥赛罗 (Othello)",是一种得分会戏剧性变化并且需要长时间思考的策略性游戏. 翻转棋的棋盘上有 64 个可以放置黑白棋子的方格(类似于国际象棋和跳棋).游戏的目标是使棋盘上自己颜色的棋子数超过对手的棋子数.

黑白棋游戏 (codevs 2743)题解

[问题描述] 黑白棋游戏的棋盘由4×4方格阵列构成.棋盘的每一方格中放有1枚棋子,共有8枚白棋子和8枚黑棋子.这16枚棋子的每一种放置方案都构成一个游戏状态.在棋盘上拥有1条公共边的2个方格称为相邻方格.一个方格最多可有4个相邻方格.在玩黑白棋游戏时,每一步可将任何2个相邻方格中棋子互换位置.对于给定的初始游戏状态和目标游戏状态,编程计算从初始游戏状态变化到目标游戏状态的最短着棋序列. [样例输入] 1111 0000 1110 0010 1010 0101 1010 0101 [样例输出] 4

枚举(黑白棋)

/*代码一:DFS+Enum*/ //Memory Time //240K 344MS //本题只要求输出翻转的次数,因此BFS或DFS都适用 #include<iostream> using namespace std; bool chess[6][6]={false};//利用的只有中心的4x4 bool flag; int step; int r[]={-1,1,0,0,0};//便于翻棋操作 int c[]={0,0,-1,1,0}; bool judge_all(void)//判断&