Problem:
勇者ビ太郎(Bitaro the Brave)

勇者のビ太郎は，魔王と対峙することとなった．

ビ太郎は，縦$H$ 行，横$W$ 列のマス目上に宝石(Jewel)，オーブ(Orb)，金塊(Ingot) を配置し，魔法を発動することによって魔王に攻撃をしようとしている．以下，マス目のうち上から$i$ 行目($1 \leq i \leq H$)，左から$j$ 列目($1 \leq j \leq W$) のマスを，マス($i, j$) と表す．

ビ太郎は今，それぞれのマスにこれら3 種類のうち1 個を配置した．今から魔法を発動しようとしているが，この魔法の威力はマス目上の宝石，オーブ，金塊の配置によって決まる．具体的には，次の条件を満たす整数($i, j, k, l$) ($1 \leq i < k \leq H, 1 \leq j < l \leq W$) の組の個数が，魔法の威力である．

条件：マス($i, j$) には宝石が，マス($i, l$) にはオーブが，マス($k, j$) には金塊が置かれている．

ビ太郎は，この魔法の威力が気になっている．

マス目上の宝石，オーブ，金塊の配置が与えられたとき，ビ太郎が発動する魔法の威力を求めるプログラムを作成せよ．

入力

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

$H$ $W$
$S_1$
:
$S_H$

$S_i$ ($1 \leq i \leq H$) は長さ$W$ の文字列で，その$j$ 文字目($1 \leq j \leq W$) がJ のときはマス($i, j$) に宝石が，O の ときはマス($i, j$) にオーブが，I のときはマス($i, j$) に金塊が置かれていることを表す．

出力

標準出力に，魔法の威力を表す整数を1 行で出力せよ．

制約

$2 \leq H \leq 3 000$．

$2 \leq W \leq 3 000$．

$S_i$ は長さ$W$ の文字列である($1 \leq i \leq H$)．

$S_i$ の各文字はJ，O，I のいずれかである($1 \leq i \leq H$)．

入出力例

入力例1

3 4
JOIJ
JIOO
IIII

出力例1

3

この入力例では，($i, j, k, l$) = (1, 1, 3, 2), (2, 1, 3, 3), (2, 1, 3, 4) の3 個の組が条件を満たすので，答えは3 である．

入力例2

4 4
JJOO
JJOO
IIJO
IIIJ

出力例2

17

情報オリンピック日本委員会作 『第18 回日本情報オリンピック(JOI 2018/2019) 本選』

Solution in C++ (judge verdict: Accepted)

#include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 3005

struct LOC{
	LOC(int arg_row,int arg_col){
		row = arg_row;
		col = arg_col;
	}
	int row,col;
};

int H,W;
ll num_O[NUM][NUM],num_I[NUM][NUM];
char table[NUM][NUM];
vector<LOC> V;


int main(){

	scanf("%d %d",&H,&W);

	for(int row = 0; row < H; row++){

		scanf("%s",table[row]);

		for(int col = 0; col < W; col++){

			if(table[row][col] == 'J'){
				V.push_back(LOC(row,col));
			}
		}

		//右→左にOの数を数える
		if(table[row][W-1] == 'O'){

			num_O[row][W-1] = 1;

		}else{

			num_O[row][W-1] = 0;
		}

		for(int col = W-2; col >= 0; col--){

			if(table[row][col] == 'O'){

				num_O[row][col] = num_O[row][col+1]+1;

			}else{

				num_O[row][col] = num_O[row][col+1];
			}
		}
	}

	//下→上にIの数を数える
	for(int col = 0; col <= W-1; col++){

		if(table[H-1][col] == 'I'){

			num_I[H-1][col] = 1;
		}else{

			num_I[H-1][col] = 0;
		}

		for(int row = H-2; row >= 0; row--){

			if(table[row][col] == 'I'){

				num_I[row][col] = num_I[row+1][col]+1;
			}else{

				num_I[row][col] = num_I[row+1][col];
			}
		}
	}

	ll ans = 0;

	for(int i = 0; i < V.size(); i++){

		ans += num_O[V[i].row][V[i].col]*num_I[V[i].row][V[i].col];
	}

	printf("%lld\n",ans);

	return 0;
}