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 JAVA (judge verdict: Time Limit Exceeded)


import java.io.*;
import java.util.*;


// 2011/10/22

// 0559 JOIø
public class Main {

	// C return falseÅ¨µÜ¢
	boolean main() throws IOException {

		int[] ir = readIntArray();
		int height = ir[0];
		int width = ir[1];

		int allQCnt = 0; // ? Ì
		String[] map = new String[height];
		for(int i = 0; i < height; i++) {
			map[i] = reader.readLine();
			for(int x = 0; x < width; x++) {
				if (map[i].charAt(x) == '?')
					allQCnt++;
			}
		}
		
		int MOD = 100000;
		
		// 3Ì×«æe[uðÂ­é allQCntÍÅå400
		int[] pow3 = new int[allQCnt];
		int m = 1;
		for(int i = 0; i < allQCnt; i++) {
			pow3[i] = m;
//			log.printf("pos3 %d %d\n", i, m);
			m = m * 3;
			m = m % MOD;
		}
		
		// JOIªÅ«»¤ÈÆ±ë  x,y,?Ìzñðo^·é
		List<int[]> list = new ArrayList<int[]>();
		
		for(int x = 0; x < width -1; x++) {
			for(int y = 0; y < height -1; y++) {
				char c1 = map[y].charAt(x);
				char c2 = map[y].charAt(x+1);
				char c3 = map[y+1].charAt(x);
				
				if ((c1 == 'J' || c1 == '?') &&
					(c2 == 'O' || c2 == '?') &&
					(c3 == 'I' || c3 == '?')) {
					int c = 0;
					if (c1 == '?')
						c++;
					if (c2 == '?')
						c++;
					if (c3 == '?')
						c++;
					list.add(new int[] { x, y, c });
				}
			}
		}
		
		// JOIªÅ«»¤ÈÆ±ë
		int dcnt = list.size();
		log.printf("JOIÌÅ«»¤ÈÆ±ë=%d S?=%d\n", dcnt, allQCnt);
		
		// 2^dcnt(JOIÅ«»¤ÈÆ±ë)ÌgÝí¹ÉÂ¢Ä
		boolean[] isUse = new boolean[dcnt];
		
		// dÈè`FbNpmap
		int[][] checkMap = new int[width][height];
		
		int sum = 0;  // ðÌ
		int useCnt = 0; // JOIÌ@XV·é
		int qCnt = allQCnt; //@JOIðìéêÈOÌ?Ì XV·é
		loop:
		for(;;) {
			// loop Ì trueÌÆ±ëÉÂ¢ÄvZ·é
			
			// next  all falseÍòÎ·ÌÅÅÉâéÆÇ¢
			for(int i = 0;; i++) {
				if (i >= dcnt)
					break loop;
				isUse[i] = !isUse[i];
				int[] joi = list.get(i);
				int x = joi[0];
				int y = joi[1];
				if (isUse[i]) {
					// dÈçÈ¢©`FbN
					if (checkMap[x][y] != 0 || checkMap[x+1][y] != 0 || checkMap[x][y+1] != 0) {
						// dÈéÌÅ¾ß
						isUse[i] = false; // ß·
					}
					else {
						// JOIÝu
						useCnt++;
						qCnt -= joi[2];
						checkMap[x  ][y  ] = 1;
						checkMap[x+1][y  ] = 1;
						checkMap[x  ][y+1] = 1;
						break;
					}
				}
				else {
					// JOI
					useCnt--;
					qCnt += joi[2];
					checkMap[x  ][y  ] = 0;
					checkMap[x+1][y  ] = 0;
					checkMap[x  ][y+1] = 0;
				}
			}

			// Êu±Â·éÌÍ¾ß ãº¶E×ÆAEãÆ¶ºª¾ß
			boolean dame = false;
//			l2:
//			for(int i = 0; i < dcnt; i++) {
//				if (!isUse[i]) 
//					continue;
//				for(int j = i + 1; j < dcnt; j++) {
//					if (!isUse[j]) 
//						continue;
//					int[] p1 = list.get(i);
//					int[] p2 = list.get(j);
//					if (Math.abs(p1[0] - p2[0]) + Math.abs(p1[1] - p2[1]) <= 1) {
//						dame = true;
//						break l2;
//					}
//					if (p1[0] - p2[0] == 1 && p1[1] - p2[1] == -1) {
//						dame = true;
//						break l2;
//					}
//					if (p1[0] - p2[0] == -1 && p1[1] - p2[1] == 1) {
//						dame = true;
//						break l2;
//					}
//				}
//			}

			if (!dame) {
			
//				int onCnt = 0;
//				int qc = 0;
//				for(int i = 0; i < dcnt; i++) {
//					if (isUse[i]) {
//						onCnt++;
//						qc += list.get(i)[2];
//					}
//				}
//				
//				if (useCnt != 0) {
					
					// gÝí¹ 3 ^ (©ªÈOÌ?Ì)
					int k = pow3[qCnt];
					//assert allQCnt - qc == qCnt;
					if (useCnt % 2 == 0) // ¦¦ ïð«µÄôðø­
						k = MOD - k;
					
					//log.printf("JOIÌ=%d ?Ì=%d gÝí¹= 3^%d = %d\n", onCnt, qc, allQCnt - qc, k);
					sum += k;
					sum = sum % MOD; // intð´¦ÄµÜ¤±Æª é
//					log.printf("sum=%d\n", sum);
//				}
			}
			
		}
		
			
		// ðÌ\¦
		System.out.printf("%d\n", sum);
		
		
		return false;
		//return true; // ³íI¹ Ö
	}
	

//	private final static boolean DEBUG = true;  // debug
	private final static boolean DEBUG = false; // release

	public static void main(String[] args) throws IOException {

		if (DEBUG) {
			log = System.out;
			
//			String inputStr = "2 3:??O:IIJ:";
//			String inputStr = "2 2:??:??:";
//			String inputStr = "3 3:??I:???:O?J:";
//			String inputStr = "5 4:JOI?:????:????:????:?JOI:";
			
//			String inputStr = "2 5:JO???:I????:";
			String inputStr = "6 8:????????:????????:????????:????????:????????:????????:";

			inputStr = inputStr.replace(":", "\n");

			reader = new BufferedReader(new StringReader(inputStr)); 

		}
		else {
			log = new PrintStream(new OutputStream() { public void write(int b) {} } ); // «ÌÄ
			reader = new BufferedReader(new InputStreamReader(System.in)); // R\[©ç
		}
		
		//int N = readIntArray()[0];

		for(int i = 0; /*i < N*/; i++) {
			boolean b = new Main().main();
			if (!b)
				break;
		}		
		
		reader.close();
	}

	
	static PrintStream log;
	static BufferedReader reader;
	

	// WüÍæè1sªÌXy[XæØèÌ®lðÇÞ
	// EOFÌêÍnullðÔ·
	private static int[] readIntArray() throws IOException {

		String s = null;
		for(;;) {
			s = reader.readLine();
			if (s.length() != 0) // ¦¦¦@Ç¤àËRósðÇÞ±Æª éBÇÝòÎ·Æ¤Ü­¢­çµ¢BBBB
				break;
		}
		
		if (s == null)
			return null;
		s = s.trim();
		String[] sp = s.split(" ");
		int[] a = new int[sp.length];
		for(int i = 0; i < sp.length; i++) {
			a[i] = Integer.parseInt(sp[i]);
		}
		return a;
	}
		
}