Problem:
JOI 旗 (JOI Flag)

問題

情報オリンピック日本委員会では，今年の日本情報オリンピック (JOI) を宣伝するために， JOI のロゴをモチーフにした旗を作ることになった．旗は「良い旗」でなければならない．「良い旗」とは，アルファベットの J, O, I のいずれかの文字を，縦 M 行，横 N 列の長方形状に並べたもので， J, O, I が以下の図のように (すなわち，J の右隣に O が，その J の下隣に I が) 並んでいる箇所が旗の少なくとも 1 か所にあるものである．

以下の図に，「良い旗」の例を 2 つ示す．

以下の図に，「良い旗」ではない例を 2 つ示す．

いま M, N の値，および旗の一部の場所について J, O, I のどの文字にするかが決まっており，入力としてその情報が与えられる．考えられる「良い旗」は何通りあるかを計算し，その数を 100000 (=105) で割った余りを出力するプログラムを作成せよ．

入力

入力は 1 + M 行からなる．

1 行目には旗の大きさを表す 2 つの整数 M, N (2 ≤ M ≤ 20, 2 ≤ N ≤ 20) が空白で区切られて書かれている．

1 + i 行目 (1 ≤ i ≤ M) には，N 文字からなる文字列が書かれている．各文字は J, O, I, ? のいずれかであり， j 文字目 (1 ≤ j ≤ N) が J, O, I のいずれかである場合は i 行 j 列の場所の文字がそれぞれ J, O, I に決まっていること， ? である場合はまだ決まっていないことを表す．

出力

考えられる「良い旗」の個数を 100000 (=105) で割った余りを 1 行で出力せよ．

入出力例

入力例 1

2 3
??O
IIJ

出力例 1

4

入力例 1 においては，以下の図の 4 通りの「良い旗」が考えられる．

入力例 2

2 2
??
??

出力例 2

3

入力例 3

3 3
??I
???
O?J

出力例 3

53

入力例 4

5 4
JOI?
????
????
????
?JOI

出力例 4

28218

入力例 4 においては，「良い旗」は 2428218 通り考えられるので，それを 100000 (=105) で割った余りである 28218 を出力する．

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C (judge verdict: Accepted)

#include <stdio.h>

/*óÔÔê*/
int status_no[20000];
int status_no_max;
/*[xÀW][yÀW][óÔÔ][¼OªJ©]*/
int memo[20][20][20000][2];

/*üÍîñ*/
char flag[20][21];
int width,height;

/*óÔðñ·é*/
void enum_status(void) {
	int current_number;
	int pos;
	int i;
	status_no[0]=0;
	status_no[1]=1;
	pos=2;
	for(current_number=2;current_number<(1<<19);current_number<<=1) {
		for(i=0;status_no[i]<(current_number>>1);i++) {
			status_no[pos]=current_number | status_no[i];
			pos++;
		}
	}
	status_no_max=pos-1;
}

/*óÔÔðæ¾·é*/
int get_status_num(int status) {
	int left=0;
	int right=status_no_max;
	int mid;
	while(left<=right) {
		mid=(left+right)/2;
		if(status==status_no[mid])return mid;
		else if(status<status_no[mid])right=mid-1;
		else left=mid+1;
	}
	return 0;
}

int search_flags(int w,int h,int x,int y,int stat,int maeisj);

int main(void) {
	int i;
	int all_flags;
	int bad_flags;
	int x,y;
	scanf("%d %d",&height,&width);
	for(i=0;i<height;i++)scanf("%s",flag[i]);
	enum_status();
	/*SÄÌøÌíÞðæ¾*/
	all_flags=1;
	for(y=0;y<height;y++) {
		for(x=0;x<width;x++) {
			if(flag[y][x]=='?') {
				all_flags=(all_flags*3)%100000;
			}
		}
	}
	/*¢¢øÅÈ¢øÌíÞðæ¾*/
	bad_flags=search_flags(width,height,0,0,0,0);
	/*¢¢øÌíÞðoÍ*/
	if(all_flags<bad_flags)all_flags+=100000;
	printf("%d\n",all_flags-bad_flags);
	return 0;
}

int search_flags(int w,int h,int x,int y,int stat,int maeisj) {
	int result=0;
	int stat2;
	if(y>=h)return 1;
	else if(x>=w)return search_flags(w,h,0,y+1,stat,0);
	if(memo[x][y][get_status_num(stat)][maeisj]>0) {
		return memo[x][y][get_status_num(stat)][maeisj]-1;
	}
	if(flag[y][x]!='?') {
		if(flag[y][x]=='J') {
			stat2=(stat<<1) & ((1<<(w-1))-1);
			result=search_flags(w,h,x+1,y,stat2,1);
		} else if(flag[y][x]=='O') {
			stat2=(stat<<1) & ((1<<(w-1))-1);
			if(maeisj)stat2|=1;
			result=search_flags(w,h,x+1,y,stat2,0);
		} else if(flag[y][x]=='I') {
			if(!(stat & (1<<(w-2)))) {
				/*ãÉJOª³¢ê*/
				stat2=(stat<<1) & ((1<<(w-1))-1);
				result=search_flags(w,h,x+1,y,stat2,0);
			}
		}
	} else {
		/*JðÄÍßé*/
		stat2=(stat<<1) & ((1<<(w-1))-1);
		result=search_flags(w,h,x+1,y,stat2,1);
		/*OðÄÍßé*/
		stat2=(stat<<1) & ((1<<(w-1))-1);
		if(maeisj)stat2|=1;
		result+=search_flags(w,h,x+1,y,stat2,0);
		/*IðÄÍßé*/
		if(!(stat & (1<<(w-2)))) {
			/*ãÉJOª³¢ê*/
			stat2=(stat<<1) & ((1<<(w-1))-1);
			result+=search_flags(w,h,x+1,y,stat2,0);
		}
	}
	result%=100000;
	memo[x][y][get_status_num(stat)][maeisj]=result+1;
	return result;
}