Loj #2731 「JOISC 2016 Day 1」棋盘游戏
Loj 2731 「JOISC 2016 Day 1」棋盘游戏
JOI 君有一个棋盘,棋盘上有 \(N\) 行 \(3\) 列 的格子。JOI 君有若干棋子,并想用它们来玩一个游戏。初始状态棋盘上至少有一个棋子,也至少有一个空位。
游戏的目标是:在还没有放棋子的格子上依次放棋子,并填满整个棋盘。在某个格子上放置棋子必须满足以下条件之一:
- 这个格子的上下一格都放有棋子;
- 这个格子的左右一格都放有棋子。
JOI 君想知道有多少种从初始状态开始,并达到游戏目标的方案,这个答案可能会非常大。请你帮 JOI 君算出这个答案,并对 \(10^9+7\) 取模。
输入格式
第一行有一个整数 \(N\) ,表示棋盘的大小为纵向 \(3\) 格,横向 \(N\) 格。
接下来的三行均为仅由 o
和 x
组成的字符串。这三行中第 \(i\) 行的第 \(j\) 个字符表示棋盘中从上到下第 \(i\) 行,从左到右第 \(j\) 个棋子的状态。其中 o
表示开始时有棋子被放置,x
表示开始时这个位置为没有放置着棋子。
输出格式
一个整数,表示符合条件的方案个数。
样例输入 1
3
oxo
xxo
oxo
样例输出 1
14
样例输入 2
10
ooxooxoxoo
xooxxxoxxx
oxoxoooooo
样例输出 2
149022720
样例输入 3
10
ooxoxxoxoo
oxxxxxoxxx
oxooxoxoxo
样例输出 3
0
样例解释 3
没有可以达到最终状态的方案。
样例输入 4
20
oxooxoxooxoxooxoxoxo
oxxxoxoxxxooxxxxxoox
oxooxoxooxooxooxoxoo
样例输出 4
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\)中情况讨论。
细节就不说了,讨论就行了。
转移的时候还要用前缀和优化。
代码:
#include<bits/stdc++.h>
#define ll long long
#define pr pair<int,int>
#define mp(a,b) make_pair(a,b)
#define N 2005
using namespace std;
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;}
const ll mod=1e9+7;
ll ksm(ll t,ll x) {
ll ans=1;
for(;x;x>>=1,t=t*t%mod)
if(x&1) ans=ans*t%mod;
return ans;
}
int n;
char mp[4][N];
ll fac[N*3],ifac[N*3];
ll C(int n,int m) {
if(n<m) return 0;
return fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}
int u[N];
ll f[N][N*3],g[N][N*3];
int res,sum[N];
ll pre[N*3];
int main() {
n=Get();
fac[0]=1;
for(int i=1;i<=3*n;i++) fac[i]=fac[i-1]*i%mod;
ifac[3*n]=ksm(fac[3*n],mod-2);
for(int i=3*n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
for(int i=1;i<=3;i++) {
scanf("%s",mp[i]+1);
}
for(int i=1;i<=n;i++) {
if(mp[1][i]=='x'&&(mp[1][i-1]!='o'||mp[1][i+1]!='o')) {cout<<0;return 0;}
if(mp[3][i]=='x'&&(mp[3][i-1]!='o'||mp[3][i+1]!='o')) {cout<<0;return 0;}
}
for(int i=1;i<=n;i++) {
if(mp[1][i]=='x') u[i]++;
if(mp[3][i]=='x') u[i]++;
}
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+u[i]+(mp[2][i]=='x');
if(mp[2][1]=='x') f[1][1]=1;
else f[1][0]=1;
for(int i=2;i<=n;i++) {
if(mp[2][i]=='x') {
if(mp[2][i-1]=='x') {
memset(pre,0,sizeof(pre));
for(int j=1;j<=sum[i-1];j++) {//f,i-1由上下贡献
if(u[i-1]==2) pre[j]=(pre[j-1]+f[i-1][j]*j%mod*(j+1))%mod;
else if(u[i-1]==1) pre[j]=(pre[j-1]+f[i-1][j]*j)%mod;
else pre[j]=(pre[j-1]+f[i-1][j])%mod;
}
for(int j=sum[i-1];j>=1;j--) if(j>u[i-1]) pre[j]=pre[j-u[i-1]];
for(int j=1;j<=u[i-1];j++) pre[j]=0;
for(int j=1;j<=sum[i-1];j++) (f[i][j]+=pre[sum[i-1]]-pre[j-1]+mod)%=mod;
for(int j=2;j<=sum[i-1]+1;j++) (g[i][j]+=pre[j-1])%=mod;
memset(pre,0,sizeof(pre));
for(int j=1;j<=sum[i-1];j++) {//g,i-1由上下贡献
if(u[i-1]==2) pre[j]=(pre[j-1]+g[i-1][j]*j%mod*(j+1))%mod;
else if(u[i-1]==1) pre[j]=(pre[j-1]+g[i-1][j]*j)%mod;
else pre[j]=(pre[j-1]+g[i-1][j])%mod;
}
for(int j=sum[i-1];j>=1;j--) if(j>u[i-1]) pre[j]=pre[j-u[i-1]];
for(int j=1;j<=u[i-1];j++) pre[j]=0;
for(int j=2;j<=sum[i-1]+1;j++) (g[i][j]+=pre[j-1])%=mod;
memset(pre,0,sizeof(pre));
for(int j=1;j<=sum[i-1];j++) {//g,i-1由左右贡献
(pre[j]+=pre[j-1])%=mod;
if(!u[i-1]) {
(pre[j]+=g[i-1][j])%=mod;
} else if(u[i-1]==1) {
(pre[j+1]+=g[i-1][j]*j)%=mod;
(pre[j]+=g[i-1][j]*(sum[i-1]-j))%=mod;
} else {
(pre[j]+=g[i-1][j]*(sum[i-1]-j-1)%mod*(sum[i-1]-j))%=mod;
(pre[j+1]+=g[i-1][j]*j%mod*(sum[i-1]-j-1)%mod*2)%=mod;
(pre[j+2]+=g[i-1][j]*j%mod*(j+1))%=mod;
}
}
for(int j=1;j<=sum[i-1];j++) (f[i][j]+=pre[sum[i-1]]-pre[j-1]+mod)%=mod;
} else {
for(int j=1;j<=sum[i-1]+1;j++) {
if(u[i-1]==0) g[i][j]=f[i-1][0];
else if(u[i-1]==1) g[i][j]=f[i-1][0]*sum[i-1]%mod;
else g[i][j]=f[i-1][0]*(sum[i-1]-1)%mod*sum[i-1]%mod;
}
}
} else {
if(mp[2][i-1]=='x') {
for(int j=1;j<=sum[i-1];j++) {
if(u[i-1]==0) {
(f[i][0]+=f[i-1][j]+g[i-1][j])%=mod;
} else if(u[i-1]==1) {
(f[i][0]+=f[i-1][j]*j+g[i-1][j]*(sum[i-1]))%=mod;
} else {
(f[i][0]+=f[i-1][j]*j%mod*(j+1)+g[i-1][j]*(sum[i-1])%mod*(sum[i-1]-1))%=mod;
}
}
} else {
if(u[i-1]==0) f[i][0]=f[i-1][0];
else if(u[i-1]==1) f[i][0]=f[i-1][0]*sum[i-1]%mod;
else f[i][0]=f[i-1][0]*(sum[i-1]-1)%mod*sum[i-1]%mod;
}
}
}
ll ans=0;
if(mp[2][n]=='o') {
if(!u[n]) (ans+=(f[n][0]))%=mod;
else if(u[n]==1) (ans+=f[n][0]*sum[n])%=mod;
else (ans+=f[n][0]*(sum[n]-1)%mod*sum[n])%=mod;
} else {
for(int i=1;i<=sum[n];i++) {
if(!u[n]) (ans+=(f[n][i]+g[n][i]))%=mod;
else if(u[n]==1) (ans+=f[n][i]*i+g[n][i]*sum[n])%=mod;
else (ans+=f[n][i]*i%mod*(i-1)+g[n][i]*sum[n]%mod*(sum[n]-1))%=mod;
}
}
cout<<ans;
return 0;
}
Loj #2731 「JOISC 2016 Day 1」棋盘游戏的更多相关文章
- LOJ #2731. 「JOISC 2016 Day 1」棋盘游戏(dp)
题意 JOI 君有一个棋盘,棋盘上有 \(N\) 行 \(3\) 列 的格子.JOI 君有若干棋子,并想用它们来玩一个游戏.初始状态棋盘上至少有一个棋子,也至少有一个空位. 游戏的目标是:在还没有放棋 ...
- 「JOISC 2016 Day 1」棋盘游戏
「JOISC 2016 Day 1」棋盘游戏 先判无解:第1,3行有连续的空格或四个角有空格. 然后可以发现有解的情况第1,3行可以在任意时间摆放. 对于某一列,若第2行放有棋子,那么显然可以把棋盘分 ...
- LOJ 2737 「JOISC 2016 Day 3」电报 ——思路+基环树DP
题目:https://loj.ac/problem/2737 相连的关系形成若干环 / 内向基环树 .如果不是只有一个环的话,就得断开一些边使得图变成若干链.边的边权是以它为出边的点的点权. 基环树的 ...
- LOJ 2736 「JOISC 2016 Day 3」回转寿司 ——堆+分块思路
题目:https://loj.ac/problem/2736 如果每个询问都是 l = 1 , r = n ,那么每次输出序列的 n 个数与本次操作的数的最大值即可.可以用堆维护. 不同区间的询问,可 ...
- [LOJ#2732] 「JOISC 2016 Day 2」雇佣计划
参考博文 (不过个人感觉我讲的稍微更清楚一点) 题目就是让我们求图中满足数值大于等于B的连通块数量 然后我们可以尝试转换为求连通块两端所产生的“谷”的数量,显然一个连通块对谷可以贡献2的答案,最终答案 ...
- loj 2392「JOISC 2017 Day 1」烟花棒
loj 答案显然满足二分性,先二分一个速度\(v\) 然后显然所有没有点火的都会往中间点火的人方向走,并且如果两个人相遇不会马上点火,要等到火快熄灭的时候才点火,所以这两个人之后应该在一起行动.另外有 ...
- 「JOISC 2016 Day 3」回转寿司
https://loj.ac/problem/2736 题解 挺有意思的题. 考虑这种操作不好直接维护,还有时限比较长,所以考虑分块. 考虑一个操作对整个块的影响,无非就是可能把最大的拿走,再把新的元 ...
- loj2734「JOISC 2016 Day 2」女装大佬 || 洛谷P3615 如厕计划
loj2734 洛谷P3615 http://218.5.5.242:9021/problem/185 不会做... 题解(来自ditoly): 这一步更详细的解释(来自kkksc03): 还是从后面 ...
- [LOJ #2833]「JOISC 2018 Day 1」帐篷
题目大意:有一个$n\times m$的网格图,若一个人的同一行或同一列有人,他就必须面向那个人,若都无人,就可以任意一个方向.若一个人无法确定方向,则方案不合法,问不同的方案数.$n,m\leqsl ...
随机推荐
- WPF DevExpress ChartControl用法
WPF常用的第三方控件集,DevExpress中ChartControl的使用 下面介绍如何生成Chart界面: <dxc:ChartControl AnimationMode="On ...
- C#.NET和C++结构体Socket通信与数据转换
最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结 构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响 ...
- SSM 设置静态资源处理
使用weblogic的虚拟路径virtual-directory-mapping 在页面上展示文件服务器上的图片 <weblogic-web-app> ......省略其他部分 <c ...
- Docker 系列二(操作镜像).
一.镜像管理 1.拉取镜像 docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] -- Docker 镜像仓库地址 :一般是 域名或者IP[:端口号 ...
- SpringBoot打包报错没有主清单
1,如果你的POM是继承spring-boot-starter-parent的话,只需要下面的指定就行. <properties> <!-- The main class to st ...
- 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...
- JS对url进行编码和解码(三种方式区别)
Javascript语言用于编码的函数,一共有三个,最古老的一个就是escape().虽然这个函数现在已经不提倡使用了,但是由于历史原因,很多地方还在使用它,所以有必要先从它讲起. escape 和 ...
- div中img依据不同分辨率居中显示,超出部分隐藏
在做banner居中时 碰到的问题,知道可以用背景图实现居中显示,但是内心是想深究下的,故找到几种办法收集一下,后面两种真的是奇技淫巧 来着下面两处 https://www.zhihu.com/que ...
- C# 8.0的三个值得关注的新特性
本文翻译自:https://dzone.com/articles/3-new-c-8-features-we-are-excited-about 转载请注明出自:葡萄城官网,葡萄城为开发者提供专业的开 ...
- Android assets文件夹之位置放置和作用
Android 的assets文件夹的放置位置,Eclipse创建项目时就生成了的,Android Studio则不太一样,AS可以包含几种方式, 1:可以在build.gradle文件下配置,加如下 ...