Loj 2731 「JOISC 2016 Day 1」棋盘游戏

JOI 君有一个棋盘,棋盘上有 \(N\) 行 \(3\) 列 的格子。JOI 君有若干棋子,并想用它们来玩一个游戏。初始状态棋盘上至少有一个棋子,也至少有一个空位。

游戏的目标是:在还没有放棋子的格子上依次放棋子,并填满整个棋盘。在某个格子上放置棋子必须满足以下条件之一:

  1. 这个格子的上下一格都放有棋子;
  2. 这个格子的左右一格都放有棋子。

JOI 君想知道有多少种从初始状态开始,并达到游戏目标的方案,这个答案可能会非常大。请你帮 JOI 君算出这个答案,并对 \(10^9+7\) 取模。

输入格式

第一行有一个整数 \(N\) ,表示棋盘的大小为纵向 \(3\) 格,横向 \(N\) 格。

接下来的三行均为仅由 ox 组成的字符串。这三行中第 \(i\) 行的第 \(j\) 个字符表示棋盘中从上到下第 \(i\) 行,从左到右第 \(j\) 个棋子的状态。其中 o 表示开始时有棋子被放置,x 表示开始时这个位置为没有放置着棋子。

输出格式

一个整数,表示符合条件的方案个数。

样例输入 1

  1. 3
  2. oxo
  3. xxo
  4. oxo

样例输出 1

  1. 14

样例输入 2

  1. 10
  2. ooxooxoxoo
  3. xooxxxoxxx
  4. oxoxoooooo

样例输出 2

  1. 149022720

样例输入 3

  1. 10
  2. ooxoxxoxoo
  3. oxxxxxoxxx
  4. oxooxoxoxo

样例输出 3

  1. 0

样例解释 3

没有可以达到最终状态的方案。

样例输入 4

  1. 20
  2. oxooxoxooxoxooxoxoxo
  3. oxxxoxoxxxooxxxxxoox
  4. oxooxoxooxooxooxoxoo

样例输出 4

  1. 228518545

\(JOI\)的\(DP\)题好恶心。

显然,\(1,3\)行的空格左右两边必须是已经放好的棋子,否则无解。也就是说\(1,3\)行的空格可以再任意时刻放棋子。

于是我们只需要对第\(2\)行中的空格进行\(DP\)就可以了。

我们设\(f_{i,j}\)表示\((2,i)\)的空格在时刻\(j\)放了一个棋子,且\((2,i-1)\)在\((2,i)\)之后放置的方案数;\(g_{i,j}\)表示\((2,i)\)的空格在时刻\(j\)放了一个棋子,且\((2,i-1)\)在\((2,i)\)之前放置的方案数。

我们的\(f_{i,j},g_{i,j}\)都是没有考虑\((1,i)\)以及\((3,i)\)的放置情况的,当我们转移\(f_{i+1,j},g_{i+1,j}\)的时候我们再考虑。因为如果\((2,i-1),(2,i+1)\)都在\((2,i)\)之前放置,则\((1,i),(3,i)\)可以任意放置,否则必须在\((2,i)\)之前放置。

转移就按\((2,i)\)和\((2,i+1)\)分\(4\)中情况讨论。

细节就不说了,讨论就行了。

转移的时候还要用前缀和优化。

代码:

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define pr pair<int,int>
  4. #define mp(a,b) make_pair(a,b)
  5. #define N 2005
  6. using namespace std;
  7. inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
  8. const ll mod=1e9+7;
  9. ll ksm(ll t,ll x) {
  10. ll ans=1;
  11. for(;x;x>>=1,t=t*t%mod)
  12. if(x&1) ans=ans*t%mod;
  13. return ans;
  14. }
  15. int n;
  16. char mp[4][N];
  17. ll fac[N*3],ifac[N*3];
  18. ll C(int n,int m) {
  19. if(n<m) return 0;
  20. return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
  21. }
  22. int u[N];
  23. ll f[N][N*3],g[N][N*3];
  24. int res,sum[N];
  25. ll pre[N*3];
  26. int main() {
  27. n=Get();
  28. fac[0]=1;
  29. for(int i=1;i<=3*n;i++) fac[i]=fac[i-1]*i%mod;
  30. ifac[3*n]=ksm(fac[3*n],mod-2);
  31. for(int i=3*n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
  32. for(int i=1;i<=3;i++) {
  33. scanf("%s",mp[i]+1);
  34. }
  35. for(int i=1;i<=n;i++) {
  36. if(mp[1][i]=='x'&&(mp[1][i-1]!='o'||mp[1][i+1]!='o')) {cout<<0;return 0;}
  37. if(mp[3][i]=='x'&&(mp[3][i-1]!='o'||mp[3][i+1]!='o')) {cout<<0;return 0;}
  38. }
  39. for(int i=1;i<=n;i++) {
  40. if(mp[1][i]=='x') u[i]++;
  41. if(mp[3][i]=='x') u[i]++;
  42. }
  43. for(int i=1;i<=n;i++) sum[i]=sum[i-1]+u[i]+(mp[2][i]=='x');
  44. if(mp[2][1]=='x') f[1][1]=1;
  45. else f[1][0]=1;
  46. for(int i=2;i<=n;i++) {
  47. if(mp[2][i]=='x') {
  48. if(mp[2][i-1]=='x') {
  49. memset(pre,0,sizeof(pre));
  50. for(int j=1;j<=sum[i-1];j++) {//f,i-1由上下贡献
  51. if(u[i-1]==2) pre[j]=(pre[j-1]+f[i-1][j]*j%mod*(j+1))%mod;
  52. else if(u[i-1]==1) pre[j]=(pre[j-1]+f[i-1][j]*j)%mod;
  53. else pre[j]=(pre[j-1]+f[i-1][j])%mod;
  54. }
  55. for(int j=sum[i-1];j>=1;j--) if(j>u[i-1]) pre[j]=pre[j-u[i-1]];
  56. for(int j=1;j<=u[i-1];j++) pre[j]=0;
  57. for(int j=1;j<=sum[i-1];j++) (f[i][j]+=pre[sum[i-1]]-pre[j-1]+mod)%=mod;
  58. for(int j=2;j<=sum[i-1]+1;j++) (g[i][j]+=pre[j-1])%=mod;
  59. memset(pre,0,sizeof(pre));
  60. for(int j=1;j<=sum[i-1];j++) {//g,i-1由上下贡献
  61. if(u[i-1]==2) pre[j]=(pre[j-1]+g[i-1][j]*j%mod*(j+1))%mod;
  62. else if(u[i-1]==1) pre[j]=(pre[j-1]+g[i-1][j]*j)%mod;
  63. else pre[j]=(pre[j-1]+g[i-1][j])%mod;
  64. }
  65. for(int j=sum[i-1];j>=1;j--) if(j>u[i-1]) pre[j]=pre[j-u[i-1]];
  66. for(int j=1;j<=u[i-1];j++) pre[j]=0;
  67. for(int j=2;j<=sum[i-1]+1;j++) (g[i][j]+=pre[j-1])%=mod;
  68. memset(pre,0,sizeof(pre));
  69. for(int j=1;j<=sum[i-1];j++) {//g,i-1由左右贡献
  70. (pre[j]+=pre[j-1])%=mod;
  71. if(!u[i-1]) {
  72. (pre[j]+=g[i-1][j])%=mod;
  73. } else if(u[i-1]==1) {
  74. (pre[j+1]+=g[i-1][j]*j)%=mod;
  75. (pre[j]+=g[i-1][j]*(sum[i-1]-j))%=mod;
  76. } else {
  77. (pre[j]+=g[i-1][j]*(sum[i-1]-j-1)%mod*(sum[i-1]-j))%=mod;
  78. (pre[j+1]+=g[i-1][j]*j%mod*(sum[i-1]-j-1)%mod*2)%=mod;
  79. (pre[j+2]+=g[i-1][j]*j%mod*(j+1))%=mod;
  80. }
  81. }
  82. for(int j=1;j<=sum[i-1];j++) (f[i][j]+=pre[sum[i-1]]-pre[j-1]+mod)%=mod;
  83. } else {
  84. for(int j=1;j<=sum[i-1]+1;j++) {
  85. if(u[i-1]==0) g[i][j]=f[i-1][0];
  86. else if(u[i-1]==1) g[i][j]=f[i-1][0]*sum[i-1]%mod;
  87. else g[i][j]=f[i-1][0]*(sum[i-1]-1)%mod*sum[i-1]%mod;
  88. }
  89. }
  90. } else {
  91. if(mp[2][i-1]=='x') {
  92. for(int j=1;j<=sum[i-1];j++) {
  93. if(u[i-1]==0) {
  94. (f[i][0]+=f[i-1][j]+g[i-1][j])%=mod;
  95. } else if(u[i-1]==1) {
  96. (f[i][0]+=f[i-1][j]*j+g[i-1][j]*(sum[i-1]))%=mod;
  97. } else {
  98. (f[i][0]+=f[i-1][j]*j%mod*(j+1)+g[i-1][j]*(sum[i-1])%mod*(sum[i-1]-1))%=mod;
  99. }
  100. }
  101. } else {
  102. if(u[i-1]==0) f[i][0]=f[i-1][0];
  103. else if(u[i-1]==1) f[i][0]=f[i-1][0]*sum[i-1]%mod;
  104. else f[i][0]=f[i-1][0]*(sum[i-1]-1)%mod*sum[i-1]%mod;
  105. }
  106. }
  107. }
  108. ll ans=0;
  109. if(mp[2][n]=='o') {
  110. if(!u[n]) (ans+=(f[n][0]))%=mod;
  111. else if(u[n]==1) (ans+=f[n][0]*sum[n])%=mod;
  112. else (ans+=f[n][0]*(sum[n]-1)%mod*sum[n])%=mod;
  113. } else {
  114. for(int i=1;i<=sum[n];i++) {
  115. if(!u[n]) (ans+=(f[n][i]+g[n][i]))%=mod;
  116. else if(u[n]==1) (ans+=f[n][i]*i+g[n][i]*sum[n])%=mod;
  117. else (ans+=f[n][i]*i%mod*(i-1)+g[n][i]*sum[n]%mod*(sum[n]-1))%=mod;
  118. }
  119. }
  120. cout<<ans;
  121. return 0;
  122. }

Loj #2731 「JOISC 2016 Day 1」棋盘游戏的更多相关文章

  1. LOJ #2731. 「JOISC 2016 Day 1」棋盘游戏(dp)

    题意 JOI 君有一个棋盘,棋盘上有 \(N\) 行 \(3\) 列 的格子.JOI 君有若干棋子,并想用它们来玩一个游戏.初始状态棋盘上至少有一个棋子,也至少有一个空位. 游戏的目标是:在还没有放棋 ...

  2. 「JOISC 2016 Day 1」棋盘游戏

    「JOISC 2016 Day 1」棋盘游戏 先判无解:第1,3行有连续的空格或四个角有空格. 然后可以发现有解的情况第1,3行可以在任意时间摆放. 对于某一列,若第2行放有棋子,那么显然可以把棋盘分 ...

  3. LOJ 2737 「JOISC 2016 Day 3」电报 ——思路+基环树DP

    题目:https://loj.ac/problem/2737 相连的关系形成若干环 / 内向基环树 .如果不是只有一个环的话,就得断开一些边使得图变成若干链.边的边权是以它为出边的点的点权. 基环树的 ...

  4. LOJ 2736 「JOISC 2016 Day 3」回转寿司 ——堆+分块思路

    题目:https://loj.ac/problem/2736 如果每个询问都是 l = 1 , r = n ,那么每次输出序列的 n 个数与本次操作的数的最大值即可.可以用堆维护. 不同区间的询问,可 ...

  5. [LOJ#2732] 「JOISC 2016 Day 2」雇佣计划

    参考博文 (不过个人感觉我讲的稍微更清楚一点) 题目就是让我们求图中满足数值大于等于B的连通块数量 然后我们可以尝试转换为求连通块两端所产生的“谷”的数量,显然一个连通块对谷可以贡献2的答案,最终答案 ...

  6. loj 2392「JOISC 2017 Day 1」烟花棒

    loj 答案显然满足二分性,先二分一个速度\(v\) 然后显然所有没有点火的都会往中间点火的人方向走,并且如果两个人相遇不会马上点火,要等到火快熄灭的时候才点火,所以这两个人之后应该在一起行动.另外有 ...

  7. 「JOISC 2016 Day 3」回转寿司

    https://loj.ac/problem/2736 题解 挺有意思的题. 考虑这种操作不好直接维护,还有时限比较长,所以考虑分块. 考虑一个操作对整个块的影响,无非就是可能把最大的拿走,再把新的元 ...

  8. loj2734「JOISC 2016 Day 2」女装大佬 || 洛谷P3615 如厕计划

    loj2734 洛谷P3615 http://218.5.5.242:9021/problem/185 不会做... 题解(来自ditoly): 这一步更详细的解释(来自kkksc03): 还是从后面 ...

  9. [LOJ #2833]「JOISC 2018 Day 1」帐篷

    题目大意:有一个$n\times m$的网格图,若一个人的同一行或同一列有人,他就必须面向那个人,若都无人,就可以任意一个方向.若一个人无法确定方向,则方案不合法,问不同的方案数.$n,m\leqsl ...

随机推荐

  1. Git Extensions 和 Tortoisegit 到底是什么?Git For VS(Git For Visual Studio)(Visual Studio 中使用 Git)

    前言: 我们使用 Git 作为版本控制的朋友们,一定都熟悉 Git Extensions 和 Tortoisegit 两款工具,但是对于初学者,可能就不那么了解了. 当然如果有幸,你接触过 SVN , ...

  2. Tomcat日志设定

    1    Tomcat 日志概述 Tomcat 日志信息分 为 两 类 : 一.是运行中的日志,它主要 记录 运行的一些信息,尤其是一些异常 错误 日志信息 .二.是 访问 日志信息,它 记录 的 访 ...

  3. mysql的coalesce使用技巧

    今天无意间发现mysql的coalesce, coalesce()解释:返回参数中的第一个非空表达式(从左向右依次类推): 使用示例:a,b,c三个变量. ,); // Return 2 select ...

  4. 26.C++- 泛型编程之类模板(详解)

    在上章25.C++- 泛型编程之函数模板(详解) 学习了后,本章继续来学习类模板   类模板介绍 和函数模板一样,将泛型思想应用于类. 编译器对类模板处理方式和函数模板相同,都是进行2次编译 类模板通 ...

  5. spring boot @ResponseBody转换JSON 时 Date 类型处理方法,Jackson和FastJson两种方式,springboot 2.0.9配置fastjson不生效官方解决办法

    spring boot @ResponseBody转换JSON 时 Date 类型处理方法 ,这里一共有两种不同解析方式(Jackson和FastJson两种方式,springboot我用的1.x的版 ...

  6. python文件

    目录 1. 文件的概念 1.1 文件的概念和作用 1.2 文件的存储方式 2. 文件的基本操作 2.1 操作文件的套路 2.2 操作文件的函数/方法 2.3 read 方法 -- 读取文件 2.4 打 ...

  7. 网络控制器intel 82599, powerpc 830的BD描述符结构

    一.Intel 82599的BD结构 1.文档名称“82599-10-gbe-controller-datasheet.pdf”,可以从intel官网上下载. https://www.intel.cn ...

  8. 小程序实践(五):for循环绑定item的点击事件

    微信展示列表效果借助于 wx:for  简单写一个列表(wxml文件中): 对应的数据源(js文件中): 写一个点击监听: 效果: 以上.可以实现列表的item点击效果,但是无法到点击的item对应的 ...

  9. Android为TV端助力 转载:Java 泛型

    一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 1 public class GenericTest { 2 3 public static void main(Stri ...

  10. Android为TV端助力 关于Fragment你所需知道的一切!

    转载自刘明渊 的博客地址:http://blog.csdn.net/vanpersie_9987 Fragment 是 Android API 中的一个类,它代表Activity中的一部分界面:您可以 ...