Problem:
座席 (Seats)　

問題文

2XXX 年，世界の国は直線状に並んでいた．N 個の国があり，1, 2, ..., N の番号が付けられている．i = 1, 2, ..., N - 1 に対し，国 i と国 i + 1 が互いに隣国である．

この年の国際情報オリンピックでは，国 i からは A_i 人の選手が参加する．国際情報オリンピックの技術委員のあなたは，競技での座席表を作成する担当である．競技会場が細長いため，一列に並んだ A_1 + A_2 + ... + A_N 個の座席に選手たちを割り当てることになった．不正防止のため，同じ国の選手や隣国の選手を隣り合う席に割り当ててはならない．

選手たちを座席に割り当てる方法は何通りあるだろうか．この数は非常に大きくなる可能性があるので，それを 10007 で割った余りを求めたい．

制約

1 ≦ N ≦ 100

1 ≦ A_i ≦ 4 (1 ≦ i ≦ N)

入力・出力

入力

入力は以下の形式で標準入力から与えられる．

N

A_1 A_2 ... A_N

出力

選手たちを座席に割り当てる方法の数を 10007 で割った余りを 1 行で出力せよ．

入出力例

入力例 1

4
2 1 1 1

出力例 1

4

国 1 から参加する 2 人の選手を 1 と 1'，国 2 から参加する 1 人の選手を 2，国 3 から参加する 1 人の選手を 3，国 4 から参加する 1 人の選手を 4 で表すことにすると，選手たちを座席に割り当てる方法としては，以下の 4 通りの並べ方が考えられる：

1, 3, 1', 4, 2

1', 3, 1, 4, 2

2, 4, 1, 3, 1'

2, 4, 1', 3, 1

入力例 2

5
1 2 3 2 1

出力例 2

0

この入力例では，条件を満たす座席表は存在しない．

入力例 3

6
1 2 3 3 2 1

出力例 2

4754

この入力例では，選手たちを座席に割り当てる方法は 24768 通りあるため，それを 10007 で割った余りである 4754 を出力する．

情報オリンピック日本委員会作 『第 18 回日本情報オリンピック JOI 2018/2019 予選競技課題』

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

#include"bits/stdc++.h"
using namespace std;
#define LL long long
#define PB push_back
#define MP make_pair

const int MOD = 10007;
int N, A[100], sum[100];

int memo1[5] ={ 0,1,2,6,24 };
int memo2[5][5] ={ {0,0,0,0,0},{0,1,0,0,0},{0,1,1,0,0},{0,1,2,1,0},{0,1,3,3,1} };
/*
a := 国i
b := i-2以下の国どうしの問題隣接箇所
c := 国i-1と国i-3以下の隣接箇所(今はok)
d := 国i-1とi-2間の隣接箇所
e := 国i-1どうしの隣接箇所
f := それ以外の隣接箇所
*/
short dp[100][400][9][8][4];
bool visited[100][400][9][8][4] ={};
int solve(int a, int b, int c, int d, int e){
	if(a == N){
		if(b == 0 && d == 0 && e == 0) return 1;
		else return 0;
	}
	if(visited[a][b][c][d][e]) return dp[a][b][c][d][e];
	visited[a][b][c][d][e] = true;
	int f = sum[a] - b - c - d - e;
	int nc = 0;
	int nd = 0;
	LL ret = 0;
	for(int i=1; i<=A[a]; i++){ //何個の塊
		LL baai = memo1[A[a]] * memo2[A[a]][i] / memo1[i];
		for(int j=0; j<5; j++){
			if(j == 0){ if(b == 0) continue; baai*=b; b--; nc+=2; }
			if(j == 1){ if(c == 0) continue; baai*=c; c--; nc++; nd++; }
			if(j == 2){ if(d == 0) continue; baai*=d; d--; nc++; nd++; }
			if(j == 3){ if(e == 0) continue; baai*=e; e--; nd+=2; }
			if(j == 4){ if(f == 0) continue; baai*=f; f--; nc+=2; }
			if(i == 1) ret += baai * solve(a+1, b+d+e, nc, nd, A[a]-i);
			for(int k=0; k<5 && i>1; k++){
				if(k == 0){ if(b == 0) continue; baai*=b; b--; nc+=2; }
				if(k == 1){ if(c == 0) continue; baai*=c; c--; nc++; nd++; }
				if(k == 2){ if(d == 0) continue; baai*=d; d--; nc++; nd++; }
				if(k == 3){ if(e == 0) continue; baai*=e; e--; nd+=2; }
				if(k == 4){ if(f == 0) continue; baai*=f; f--; nc+=2; }
				if(i == 2) ret += baai * solve(a+1, b+d+e, nc, nd, A[a]-i);
				for(int l=0; l<5 && i>2; l++){
					if(l == 0){ if(b == 0) continue; baai*=b; b--; nc+=2; }
					if(l == 1){ if(c == 0) continue; baai*=c; c--; nc++; nd++; }
					if(l == 2){ if(d == 0) continue; baai*=d; d--; nc++; nd++; }
					if(l == 3){ if(e == 0) continue; baai*=e; e--; nd+=2; }
					if(l == 4){ if(f == 0) continue; baai*=f; f--; nc+=2; }
					if(i == 3) ret += baai * solve(a+1, b+d+e, nc, nd, A[a]-i);
					for(int m=0; m<5 && i>3; m++){
						if(m == 0){ if(b == 0) continue; baai*=b; b--; nc+=2; }
						if(m == 1){ if(c == 0) continue; baai*=c; c--; nc++; nd++; }
						if(m == 2){ if(d == 0) continue; baai*=d; d--; nc++; nd++; }
						if(m == 3){ if(e == 0) continue; baai*=e; e--; nd+=2; }
						if(m == 4){ if(f == 0) continue; baai*=f; f--; nc+=2; }
						ret += baai * solve(a+1, b+d+e, nc, nd, A[a]-i);
						if(m == 0){ b++; nc-=2; baai/=b; }
						if(m == 1){ c++; nc--; nd--; baai/=c; }
						if(m == 2){ d++; nc--; nd--; baai/=d; }
						if(m == 3){ e++; nd-=2; baai/=e; }
						if(m == 4){ f++; nc-=2; baai/=f; }
					}
					if(l == 0){ b++; nc-=2; baai/=b; }
					if(l == 1){ c++; nc--; nd--; baai/=c; }
					if(l == 2){ d++; nc--; nd--; baai/=d; }
					if(l == 3){ e++; nd-=2; baai/=e; }
					if(l == 4){ f++; nc-=2; baai/=f; }
				}
				if(k == 0){ b++; nc-=2; baai/=b; }
				if(k == 1){ c++; nc--; nd--; baai/=c; }
				if(k == 2){ d++; nc--; nd--; baai/=d; }
				if(k == 3){ e++; nd-=2; baai/=e; }
				if(k == 4){ f++; nc-=2; baai/=f; }
			}
			if(j == 0){ b++; nc-=2; baai/=b; }
			if(j == 1){ c++; nc--; nd--; baai/=c; }
			if(j == 2){ d++; nc--; nd--; baai/=d; }
			if(j == 3){ e++; nd-=2; baai/=e; }
			if(j == 4){ f++; nc-=2; baai/=f; }
		}
	}
	return dp[a][b][c][d][e] = ret%MOD;
}

int main(){
	scanf("%d", &N);
	for(int i=0; i<N; i++) scanf("%d%", A+i);
	sum[0] = 1;
	for(int i=1; i<N; i++) sum[i] = sum[i-1] + A[i-1];
	printf("%d\n", solve(0,0,0,0,0));
}