Problem:
Score : 1000 points

Problem Statement

Problem F and F2 are the same problem, but with different constraints and time limits.

We have a board divided into N horizontal rows and N vertical columns of square cells.
The cell at the i-th row from the top and the j-th column from the left is called Cell (i,j).
Each cell is either empty or occupied by an obstacle.
Also, each empty cell has a digit written on it.
If A_{i,j}= 1, 2, ..., or 9, Cell (i,j) is empty and the digit A_{i,j} is written on it.
If A_{i,j}= #, Cell (i,j) is occupied by an obstacle.

Cell Y is reachable from cell X when the following conditions are all met:

Cells X and Y are different.

Cells X and Y are both empty.

One can reach from Cell X to Cell Y by repeatedly moving right or down to an adjacent empty cell.

Consider all pairs of cells (X,Y) such that cell Y is reachable from cell X.
Find the sum of the products of the digits written on cell X and cell Y for all of those pairs.

Constraints

1 \leq N \leq 500

A_{i,j} is one of the following characters: 1, 2, ... 9 and #.

Input

Input is given from Standard Input in the following format:

N
A_{1,1}A_{1,2}...A_{1,N}
A_{2,1}A_{2,2}...A_{2,N}
:
A_{N,1}A_{N,2}...A_{N,N}

Output

Print the sum of the products of the digits written on cell X and cell Y for all pairs (X,Y) such that cell Y is reachable from cell X.

Sample Input 1

2
11
11

Sample Output 1

5

There are five pairs of cells (X,Y) such that cell Y is reachable from cell X, as follows:

X=(1,1), Y=(1,2)

X=(1,1), Y=(2,1)

X=(1,1), Y=(2,2)

X=(1,2), Y=(2,2)

X=(2,1), Y=(2,2)

The product of the digits written on cell X and cell Y is 1 for all of those pairs, so the answer is 5.

Sample Input 2

4
1111
11#1
1#11
1111

Sample Output 2

47

Sample Input 3

10
76##63##3#
8445669721
75#9542133
3#285##445
749632##89
2458##9515
5952578#77
1#3#44196#
4355#99#1#
#298#63587

Sample Output 3

36065

Sample Input 4

10
4177143673
7#########
5#1716155#
6#4#####5#
2#3#597#6#
6#9#8#3#5#
5#2#899#9#
1#6#####6#
6#5359657#
5#########

Sample Output 4

6525

Solution in C++14 (GCC 5.4.1) (judge verdict: Wrong Answer)

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#define pb push_back
template <typename T1, typename T2> inline void umin(T1 &x, const T2 &y) { y < x ? (x = y) : 0; }
template <typename T1, typename T2> inline void umax(T1 &x, const T2 &y) { y > x ? (x = y) : 0; }
int lp[2005][2005], rp[2005][2005], ul[2005], ur[2005], tmp[2005][2005];
int sum_can[2005], sum_l[2005][2005], sum_r[2005][2005], sum_in[2005][2005];
int bottom[2005], top[2005], que[2005], can_go[2005], unique[2005];
int bel_mn[2005][2005], ans;
bool del[2005];
std::vector<int> vec[2005];
inline int calc_meet(int l, int r)
{
	int pos = bel_mn[l][r];
	if (pos <= bottom[l] && pos <= bottom[r])
		return pos;
	return 1e9;
}
inline int calc_cnt(int l, int r, int lim = 1e9)
{
	lim = std::min({lim, bottom[l], bottom[r]});
	if (calc_meet(l, r) > lim)
		return 0;
	return sum_can[lim] - sum_l[lim][l] - sum_r[lim][r] + sum_in[l][r];
}
template <typename mapT>
void work(const mapT &inp)
{
	mapT str = inp;
	int n = str.size(), m = str[0].size();
	if (n < m)
	{
		mapT str_new(m, std::string(n, '#'));
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
				str_new[j][i] = str[i][j];
		}
		str = str_new;
		std::swap(n, m);
	}
	if (m == 1)
	{
		int cur = 0;
		for (int i = 0; i < n; i++)
		{
			if (str[i][0] == '#')
				cur = 0;
			else
			{
				ans += (str[i][0] - '0') * cur;
				cur += str[i][0] - '0';
			}
		}
		return;
	}
	int hu = n >> 1, hd = n - hu;
	mapT up(str.begin(), str.begin() + hu), down(str.begin() + hu, str.end());
	work(up);
	work(down);
	up.pb(down[0]);
	for (int i = 0; i < hd; i++)
	{
		for (int j = 0; j < m; j++)
		{
			lp[i][j] = 1e9;
			rp[i][j] = -1e9;
		}
	}
	// lp & rp
	for (int i = 0; i < m; i++)
	{
		if (down[0][i] != '#')
			lp[0][i] = rp[0][i] = i;
	}
	for (int i = 0; i < hd; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (down[i][j] == '#')
				continue;
			if (i)
			{
				umin(lp[i][j], lp[i - 1][j]);
				umax(rp[i][j], rp[i - 1][j]);
			}
			if (j)
			{
				umin(lp[i][j], lp[i][j - 1]);
				umax(rp[i][j], rp[i][j - 1]);
			}
		}
	}
	// top
	for (int i = 0; i <= hu; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (up[i][j] == '#')
			{
				tmp[i][j] = 1e9;
				continue;
			}
			tmp[i][j] = i;
			if (i)
				umin(tmp[i][j], tmp[i - 1][j]);
			if (j)
				umin(tmp[i][j], tmp[i][j - 1]);
		}
	}
	for (int i = 0; i < m; i++)
		top[i] = tmp[hu][i];
	// bottom
	for (int i = hd - 1; i >= 0; i--)
	{
		for (int j = m - 1; j >= 0; j--)
		{
			if (down[i][j] == '#')
			{
				tmp[i][j] = -1e9;
				continue;
			}
			tmp[i][j] = i;
			if (i + 1 < hd)
				umax(tmp[i][j], tmp[i + 1][j]);
			if (j + 1 < m)
				umax(tmp[i][j], tmp[i][j + 1]);
		}
	}
	for (int i = 0; i < m; i++)
		bottom[i] = tmp[0][i];
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < m; j++)
			bel_mn[i][j] = 1e9;
	}
	for (int i = 0; i < hd; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (lp[i][j] <= rp[i][j])
				umin(bel_mn[lp[i][j]][rp[i][j]], i);
		}
	}
	for (int l = 0; l < m; l++)
	{
		for (int r = m - 1; r >= l; r--)
		{
			if (l)
				umin(bel_mn[l][r], bel_mn[l - 1][r]);
			if (r + 1 < m)
				umin(bel_mn[l][r], bel_mn[l][r + 1]);
		}
	}
	for (int i = 0; i < hd; i++)
	{
		sum_can[i] = 0;
		memset(sum_l[i], 0, m << 2);
		memset(sum_r[i], 0, m << 2);
	}
	for (int i = 0; i < m; i++)
		memset(sum_in[i], 0, m << 2);
	for (int i = 0; i < hd; i++)
	{
		if (i)
		{
			sum_can[i] = sum_can[i - 1];
			memcpy(sum_l[i], sum_l[i - 1], m << 2);
			memcpy(sum_r[i], sum_r[i - 1], m << 2);
		}
		for (int j = 0; j < m; j++)
		{
			if (lp[i][j] > rp[i][j])
				continue;
			int val = down[i][j] - '0';
			sum_can[i] += val;
			if (lp[i][j])
				sum_l[i][lp[i][j] - 1] += val;
			if (rp[i][j] + 1 < m)
				sum_r[i][rp[i][j] + 1] += val;
			if (lp[i][j] && rp[i][j] + 1 < m)
				sum_in[lp[i][j] - 1][rp[i][j] + 1] += val;
		}
	}
	for (int i = 0; i < hd; i++)
	{
		for (int j = m - 2; j >= 0; j--)
			sum_l[i][j] += sum_l[i][j + 1];
		for (int j = 1; j < m; j++)
			sum_r[i][j] += sum_r[i][j - 1];
	}
	for (int l = m - 1; l >= 0; l--)
	{
		for (int r = l; r < m; r++)
		{
			if (l + 1 < m)
				sum_in[l][r] += sum_in[l + 1][r];
			if (r)
				sum_in[l][r] += sum_in[l][r - 1];
			if (l + 1 < m && r)
				sum_in[l][r] -= sum_in[l + 1][r - 1];
		}
	}
	for (int i = 0; i < m; i++)
		can_go[i] = calc_cnt(i, i);
	for (int i = 0; i < hu; i++)
		vec[i].clear();
	for (int i = 0; i < m; i++)
	{
		del[i] = top[i] >= hu;
		if (!del[i])
			vec[top[i]].pb(i);
	}
	for (int i = m - 1; i >= 0; i--)
	{
		if (up[hu][i] == '#')
		{
			ul[i] = 1e9;
			ur[i] = -1e9;
			continue;
		}
		ul[i] = ur[i] = i;
		if (i + 1 < m)
			umax(ur[i], ur[i + 1]);
	}
	for (int i = hu - 1; i >= 0; i--)
	{
		for (int j = m - 1; j >= 0; j--)
		{
			if (up[i][j] == '#')
			{
				ul[j] = 1e9;
				ur[j] = -1e9;
				continue;
			}
			if (j + 1 < m)
			{
				umin(ul[j], ul[j + 1]);
				umax(ur[j], ur[j + 1]);
			}
		}
		int cur = 0, cl = 0, cr = -1, he = 0, ta = 0;
		for (int j = 0; j < m; j++)
		{
			if (ul[j] > ur[j])
				continue;
			while (cr < ur[j])
			{
				int x = ++cr;
				if (del[x])
					continue;
				if (he < ta)
				{
					cur -= unique[que[ta - 1]];
					unique[que[ta - 1]] -= calc_cnt(que[ta - 1], x);
					cur += unique[que[ta - 1]];
					if (bottom[que[ta - 1]] <= bottom[x])
						ta--;
				}
				while (he < ta && bottom[que[ta - 1]] <= bottom[x])
				{
					ta--;
					cur -= unique[que[ta - 1]];
					unique[que[ta - 1]] -= calc_cnt(que[ta - 1], x);
					unique[que[ta - 1]] += calc_cnt(que[ta - 1], x, bottom[que[ta]]);
					cur += unique[que[ta - 1]];
				}
				que[ta++] = x;
				unique[x] = can_go[x];
				cur += unique[x];
			}
			while (cl < ul[j])
			{
				int x = cl++;
				if (del[x])
					continue;
				cur -= unique[x];
				if (he < ta && que[he] == x)
					he++;
			}
			ans += (up[i][j] - '0') * cur;
		}
		for (int x : vec[i])
			del[x] = true;
	}
}
char str[2005];
int main()
{
	// freopen("AGC028-F.in", "r", stdin);
	int n;
	scanf("%d", &n);
	std::vector<std::string> inp;
	for (int i = 0; i < n; i++)
	{
		scanf("%s", str);
		inp.pb(std::string(str));
	}
	work(inp);
	printf("%d\n", ans);
	return 0;
}