Problem:
Problem F: Prize Game

Problem

新しいゲームセンターが開店することになった。たくさんのお客さんを取り入れるために、全く新しいプライズゲームを設置することになった。

このプライズゲームはR×Cのグリッドから構成される。各マスは空白か、1〜18のいずれかの数字が書かれている。プレイヤーはひとつの空白のマスを選択し、そのマスの景品を獲得できる。ただし、マスにある景品はプレイヤーから見ることができない。

スタッフはこのプライズゲームに景品を設置しなければならない。スタッフは以下のルールが守られていれば、どのように景品を配置してもよい。数字が書かれているマスについて、その数字をxとすると、その数字を中心とする凸型の範囲の中に景品がちょうどx個置かれている必要がある（下の図を参照）。この凸型の出っ張っている部分は上を向いている。また、景品は空白のマスのみに置くことができ、1つのマスに3個まで置くことが出来る。1個も置かないことも可能である。

中央の数字が5だった場合の景品の置き方の例。オレンジ色の部分にちょうど計5個の景品が置かれている必要がある。

お客さんに景品の場所が簡単に推測されては大損である。そこで、オープニングスタッフであるあなたに、上記ルールに則った景品の置き方の場合の数を数えてもらいたい。ただし、景品は互いに区別できないものとする。答えは大きくなる場合があるので答えの場合の数を1000000007で割った余りを答えなさい。

Input

R C
a1,1 a1,2 ... a1,C
a2,1 a2,2 ... a2,C
:
aR,1 aR,2 ... aR,C

1行目に2つの整数R,Cが空白区切りで与えられる。それぞれグリッドの行数と列数を表す。次にプライズゲームを表すグリッドの情報がR行で与えられる。グリッドの情報のi行目にはC個の整数 ai,jが空白区切りで与えられる。ai,jはグリッドのi行j列のマス情報を表す。0の場合は空白のマス、それ以外の場合は数字ai,jが書かれたマスを表す。また、与えられるグリッドは1行目がプライズゲームの一番上を表し、R行目が一番下を表す。

Constraints

入力は以下の条件を満たす。

1 ≤ R,C ≤ 6

0 ≤ ai,j ≤ 18 (1 ≤ i ≤ R , 1 ≤ j ≤ C)

Output

ルールに則った景品の置き方の場合の数を1000000007で割った余りを1行に出力せよ。

Sample Input 1

3 3
0 0 0
0 18 0
0 0 0

Sample Output 1

16

Sample Input 2

3 3
0 0 0
0 2 0
0 0 0

Sample Output 2

336

Sample Input 3

3 3
0 1 0
1 0 0
0 1 1

Sample Output 3

1

Sample Input 4

1 1
1

Sample Output 4

0

Sample Input 5

6 6
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0

Sample Output 5

80065005

Solution in C++14 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define all(x) (x).begin(),(x).end()
#define pb push_back
#define fi first
#define se second
#define MOD 1000000007LL

typedef pair<int,int> P;

int r,c;
int a[6][6];
int b[6][6];
vector<P> pos[6][6];
ll dp[2][1<<20];
int dx[6]={0,0,1,1,-1,-1};
int dy[6]={-1,1,0,1,0,1};

bool check(int y,int x){
    int sum=0;
    for(int i=0;i<6;i++){
        int nx=x+dx[i];
        int ny=y+dy[i];
        if(nx>=0 && nx<c && ny>=0 && ny<r){
            sum+=b[ny][nx];
        }
    }
    return sum==a[y][x];
}

bool valid(int y,int x,int bit,int num){
    memset(b,0,sizeof(b));
    b[y][x]=num;
    for(int i=0;i<(int)pos[y][x].size();i++){
        int v=((bit>>(i*2))&1)+((bit>>(i*2+1))&1)*2;
        b[pos[y][x][i].fi][pos[y][x][i].se]=v;
    }
    if(y==(r-1) && x==(c-1) && a[y][x]>0){
        if(!check(y,x))return false;
    }
    if(y==(r-1) && x>0 && a[y][x-1]>0){
        if(!check(y,x-1))return false;
    }
    if(y>0 && x==(c-1) && a[y-1][x]>0){
        if(!check(y-1,x))return false;
    }
    if(y>0 && x>0 && a[y-1][x-1]>0){
        if(!check(y-1,x-1))return false;
    }
    return true;
}

int calcbit(int y,int x,int bit,int val){
    int ans=0;
    if(y==(r-1) && x==(c-1))return 0;
    int nx=(x+1)%c;
    int ny=y+(x+1)/c;
    for(int i=0;i<pos[y][x].size();i++){
        for(int j=0;j<pos[ny][nx].size();j++){
            if(pos[y][x][i]==pos[ny][nx][j]){
                int v=((bit>>(i*2))&1)+((bit>>(i*2+1))&1)*2;
                ans+=v<<(j*2);
            }
        }
    }
    for(int i=0;i<pos[ny][nx].size();i++){
        if(pos[ny][nx][i]==P(y,x))ans+=val<<(i*2);
    }
    return ans;
}

int main(){
    scanf("%d%d",&r,&c);
    for(int i=0;i<r;i++){
        for(int j=0;j<c;j++){
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=0;i<r;i++){
        for(int j=0;j<c;j++){
            if(i==0){
                for(int k=0;k<j;k++){
                    if(a[0][k]==0)pos[i][j].push_back(P(0,k));
                }
            }else{
                for(int k=0;k<j;k++){
                    if(a[i][k]==0)pos[i][j].push_back(P(i,k));
                    else{
                        if(k>0 && a[i][k-1]==0)pos[i][j].push_back(P(i,k-1));
                        if(k+1<c && a[i][k+1]==0 && (k+1)!=j)pos[i][j].push_back(P(i,k+1));
                        if(a[i-1][k]==0)pos[i][j].push_back(P(i-1,k));
                    }
                }
                for(int k=max(0,j-1);k<c;k++){
                    if(a[i-1][k]==0)pos[i][j].push_back(P(i-1,k));
                    else{
                        if(k>0 && a[i-1][k-1]==0)pos[i][j].push_back(P(i-1,k-1));
                        if(k+1<c && a[i-1][k+1]==0)pos[i][j].push_back(P(i-1,k+1));
                        if(i>1 && a[i-2][k]==0)pos[i][j].push_back(P(i-2,k));
                    }
                }
            }
            sort(pos[i][j].begin(),pos[i][j].end());
            pos[i][j].erase(unique(pos[i][j].begin(),pos[i][j].end()),pos[i][j].end());
        }
    }
    int cur=0,nxt=1;
    dp[0][0]=1;
    for(int i=0;i<r;i++){
        for(int j=0;j<c;j++){
            for(int bit=0;bit<(1<<(2*(int)pos[i][j].size()));bit++){
                int loop=4;
                if(a[i][j]>0)loop=1;
                for(int k=0;k<loop;k++){
                    if(valid(i,j,bit,k)){
                        int nbit=calcbit(i,j,bit,k);
                        dp[nxt][nbit]+=dp[cur][bit];
                        dp[nxt][nbit]%=MOD;
                    }
                }
            }
            swap(cur,nxt);
            memset(dp[nxt],0,sizeof(dp[nxt]));
        }
    }
    ll ans=0;
    for(int i=0;i<(1<<2*c);i++){
        ans=(ans+dp[cur][i])%MOD;
    }
    printf("%lld\n",ans);
    return 0;
}