[Sdoi2011]黑白棋 题解

2281: [Sdoi2011]黑白棋

Time Limit: 3 Sec  Memory Limit: 512 MB
Submit: 592  Solved: 362
[Submit][Status][Discuss]

Description

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

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

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

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

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

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

Input

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

Output

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

Sample Input

10 4 2

Sample Output

182

HINT

1<=d<=k<=n<=10000, k为偶数,k<=100。

Source

  感受到了被博弈论支配的恐惧……

  上来将题意理解为了白棋统一向右走,黑棋统一向左走,只要移动黑白棋就会碰到一起,然后就成了一个裸的nim博弈,然后就没有然后了……

  后来看黄学长的题解领悟到正确思路后默默的跪膜……

  首先我们先确认一下游戏结束的条件,如果A失败就是所有黑白棋都挨在一起且都挤在左侧,反之则都挤在右侧,原理显然。

  那么我们可以发现,如果我们想胜利白棋一定不会向左移,黑棋一定不会向右移,所以,我们可以发现当每一个白棋都和他左边的黑棋碰到一起时游戏实际上就已经结束了。所以,我们可以将相邻的两个棋子配成k/2对,目标实际就转化为了有k/2堆石子一次最少取一个堆里的任意颗石子,最多取d堆里的任意石子。谁先无法行动谁就失败。这就是传说中的nimk模型。(lc:只要你多看几眼论文就会发现这些奇奇怪怪的名词)

  对于nimk模型,先手必败当且仅当对于二进制每一位,如果所有堆的石子数二进制这一位1的和都能被(d+1)整除。

  证明:

    这种“谁不能移谁就输”的游戏先手必败就是当后手可以以某种对称的方式使你每走一步他都可以走一步实质完全一样的与你对应。表现在这道题里就是当你在二进制里的第i位取x个1时,他只要取d+1-x个就好了。

  不过这道题我们并不是要去判断是否合法, 而是计算方案数。

  为了方便,我们可以通过融斥去求答案。我们设f[i][j]为考虑了所有堆二进制前i位,已经在堆里面一共放了j个石子的方案数。转移方程即为:f[i+1][j+k*(d+1)*(1<<i)]+=f[i][j]*C(K/2,k*(d+1))。这里的K我指的是题目中的k,k*(d+1)就是枚举一共放多少组,C(K/2,k*(d+1))代表究竟放到哪一堆里。最后累加的时候记得再算上我们剩余的石子的摆放数,最终拿C(n,k)减去不合法方案数即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define N 10005
 8 using namespace std;
 9 int n,m,d,p=1000000007;
10 long long ksm(long long a,long long z)
11 {
12     long long ans=1;
13     while(z)
14     {
15         if(z&1) ans*=a,ans%=p;
16         a*=a;
17         a%=p;
18         z>>=1;
19     }
20     return ans;
21 }
22 long long jc[N],ni[N];
23 long long get_c(int x,int y)
24 {
25     return ((jc[x]*ni[y]%p)*ni[x-y]%p)%p;
26 }
27 long long f[25][N];
28 int main()
29 {
30     scanf("%d%d%d",&n,&m,&d);
31     if(m==2)
32     {
33         printf("%lld\n",1ll*(n-1)*(n-2)/2);
34         exit(0);
35     }
36     jc[0]=ni[0]=1;
37     for(int i=1;i<=n;i++) jc[i]=(jc[i-1]*i)%p;
38     ni[n]=ksm(jc[n],p-2);
39     for(int i=n-1;i;i--) ni[i]=ni[i+1]*(i+1)%p;
40     f[0][0]=1;
41     for(int i=0;i<=15;i++)
42     {
43         for(int j=0;j<=n-m;j++)
44         {
45             for(int k=0;j+(d+1)*k*(1<<i)<=n-m&&(d+1)*k<=m/2;k++)
46             {
47                 f[i+1][j+(d+1)*k*(1<<i)]+=f[i][j]*get_c(m/2,k*(d+1));
48                 f[i+1][j+(d+1)*k*(1<<i)]%=p;
49             }
50         }
51     }
52     long long ans=0;
53     for(int i=0;i<=n-m;i++)
54         ans+=f[16][i]*get_c(n-i-m/2,m/2)%p,ans%=p;
55     printf("%lld\n",(get_c(n,m)-ans+p)%p);
56     return 0;
57 }

原文地址:https://www.cnblogs.com/liutianrui/p/8300821.html

时间: 2024-08-26 14:34:58

[Sdoi2011]黑白棋 题解的相关文章

[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

[SDOI2011]黑白棋

题目描述 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同. 小A可以移动白色棋子,小B可以移动黑色的棋子,他们每次操作可以移动1到d个棋子. 每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界.当谁不可以操作时,谁就失败了. 小A和小B轮流操作,现在小A先移动,有多少种初始棋子的布局会使他胜利呢? 输入输出格式 输入格式: 共一行,三个数,n,k,d. 输出

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

黑白棋游戏 (codevs 2743)题解

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

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

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

人机对战-黑白棋

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

枚举(黑白棋)

/*代码一: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)//判断&