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

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」棋盘游戏的更多相关文章

  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. Ubuntu 18.1远程登录服务器--ssh的安装

    默认的Ubuntu 18.1桌面版没有安装ssh远程登录服务: 打开"终端窗口",输入"sudo apt-get update"-->回车-->&q ...

  2. Java岗 面试考点精讲(网络篇03期)

    1. OSI七层模型 总结一下: 应用用层按协议打包数据 由传输层加上双方的端口号 由网络层加上双方的IP地址 由链路层加上双方的MAC地址,并将数据拆分成数据帧 数模信号转换并由物理层传输到另一端 ...

  3. canvas-a12ellipse.html

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. vue的data的数据进行指定赋值,用于筛选条件的清空,或者管理系统添加成功后给部分数据赋值为空

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. POJ3734 Blocks(生成函数)

    题意 链接 长度为\(n\)的序列,用红黄蓝绿染色,其中红黄只能是偶数,问方案数 Sol 生成函数入门题 任意的是\(e^x\),偶数的是\(\frac{e^x + e^{-x}}{2}\) 最后化完 ...

  6. Visual Studio 20周年,我和VS不得不说的故事

    Visual Studio 2017正式版已如期发布(点击这里查看发布全记录)!自去年 11 月正式宣布 Visual Studio 项目之后,微软终于正式推出了“宇宙最强集成开发环境(IDE)”的最 ...

  7. Keystone, Start, Failed to Load Bson

    If you have installed the Keystone.js, and properly installed mongodb, but when tried to start the k ...

  8. BitnamiRedmine配置邮件系统备忘

    前几天安装的Redmine邮件系统存在问题,需要换一个,方法记录如下: 修改配置文件: cd ~/redmine/apps/redmine/htdocs/config vi configuration ...

  9. matlab练习程序(求向量间的旋转矩阵与四元数)

    问题是这样,如果我们知道两个向量v1和v2,计算从v1转到v2的旋转矩阵和四元数,由于旋转矩阵和四元数可以互转,所以我们先计算四元数. 我们可以认为v1绕着向量u旋转θ​角度到v2,u垂直于v1-v2 ...

  10. VirtualBox网络连接方式

    VirtualBox图形界面下有四种网络接入方式,它们分别是: 1.NAT 网络地址转换模式(NAT,Network Address Translation) 2.Bridged Adapter 桥接 ...