#include
using namespace std;
const int MOD = 1e8; //按1e8取模
const int N = 14; //M*N个小方格,上限都是12,这里我们故意取大一点,到14.
const int M = 1 << 12; //0~2^12-1,共2^12个状态
int n, m; //n行,m列
int g[N]; //记录哪个位置是无法种田的,在题目中,输入的位置是0表示无法种田
vector st; //哪些状态是本行合法的
vector head[M]; //某一个状态可以转化为哪些状态?
int f[N][M]; //状态压缩的DP数组,一维:完成了第i行,二维:在状态是j(二进制状态)的情况下
//状态检查是否合法,某个状态是不是存在连续1
bool check(int s) {
for (int i = 0; i < m; i++)//遍历每一列
//判断连续1的位运算办法,好科学啊~
if ((s >> i & 1) && (s >> (i + 1) & 1)) return false;
return true;
}
int main() {
//优化输入
ios::sync_with_stdio(false);
cin >> n >> m;
//输入地图
for (int i = 1; i <= n; i++) //n行
for (int j = 1; j <= m; j++) { //共m列
int t;
cin >> t;
//如果这个位置不能选,也按照状态压缩的思路,转为整数数值存入到g数组中
//后面在计算第i行某一位是不是无法耕种时,也可以快速用&运算获得结果
if (t == 0)g[i] += 1 << (j - 1);//这是一个值得学习的技巧
}
//预处理,哪些状态是合法状态
for (int i = 0; i < 1 << m; i++)
if (check(i)) st.push_back(i);
//预处理,所有合法状态,可以转化为哪些合法状态
for (int i = 0; i < st.size(); i++)
for (int j = 0; j < st.size(); j++) {
int a = st[i], b = st[j];
//此步骤只处理两行之间不冲突就算合理转化,不考虑无法耕种情况
if ((a & b) == 0) head[i].push_back(j);
}
//开始DP
f[0][0] = 1;//啥也不放算一种方案
for (int i = 1; i <= n + 1; i++) //遍历每一行
for (int j = 0; j < st.size(); j++)//遍历每一个状态
for (int k = 0; k < head[j].size(); k++) {//j状态的每一个可行转化
//如果此行的第j位无法耕种,那么不能转化
if (g[i] & st[j])continue;//这个&运算用的太漂亮了
//可以转化
f[i][j] += f[i - 1][head[j][k]];
//取模
f[i][j] = f[i][j] % MOD;
}
//最后一行取结果
printf("%d", f[n + 1][0] % MOD);
return 0;
}