#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <numeric>

using lint = int64_t;

using std::cin;    using std::cout;    using std::endl;
using std::sort;   using std::reverse;
using std::max;    using std::min;
using std::string;
using std::vector;
using std::pair;
using std::map;
using std::set;
using std::queue;
using std::stack;

#define low std::lower_bound
#define upp std::upper_bound
#define rep(i, n)  for (lint i = 0; i < (n); ++i)
#define rep1(i, n) for (lint i = 1; i < (n); ++i)
#define Rep(i, n)  for (lint i = (n); i >= 0; --i)
#define all(vec) vec.begin(), vec.end()
#define cend printf("\n")

using P = pair<int, int>;
using D = pair<double, double>;
//constexpr lint mod = 998'244'353LL;
constexpr lint mod = 1000'000'007LL;
constexpr lint inf = 7LL * mod * mod;
const double Pi = 2.0 * acos(0.0);

template<class T> using prique     = std::priority_queue<T>;
template<class T> using prique_inv = std::priority_queue<T, vector<T>, std::greater<T>>;
template<class T> inline bool chmax(T& a, const T& b) {
	if (a < b) {
		a = b;
		return true;
	}
	return false;
}
template<class T> inline bool chmin(T& a, const T& b) {
	if (a > b) {
		a = b;
		return true;
	}
	return false;
}

template<class T, class U>
inline std::istream& operator>>(std::istream& is, pair<T, U>& rhs) { return is >> rhs.first >> rhs.second; }
template<class T, class U>
inline std::ostream& operator<<(std::ostream& os, const pair<T, U>& rhs) { return os << rhs.first << " " << rhs.second << "\n"; }

constexpr lint gcd(lint a, lint b) { return b == 0 ? a : gcd(b, a % b); }
constexpr lint lcm(lint a, lint b) { return a / gcd(a, b) * b; }

constexpr bool pri(lint x) { for (lint i = 2; i * i <= x; ++i) if (x % i == 0) return false; return 1 < x; }

constexpr lint digit(lint x) {
	lint res = 0;
	while (x != 0) x /= 10, ++res;
	return res;
}

bool cross(D a_, D b_, D c_, D d_) {
	struct point {
		double x, y;
		point(D d) :x(d.first), y(d.second) {}
	};
	point a(a_), b(b_), c(c_), d(d_);

	double s, t;
	s = (a.x - b.x) * (c.y - a.y) - (a.y - b.y) * (c.x - a.x);
	t = (a.x - b.x) * (d.y - a.y) - (a.y - b.y) * (d.x - a.x);
	if (s * t > 0) return false;

	s = (c.x - d.x) * (a.y - c.y) - (c.y - d.y) * (a.x - c.x);
	t = (c.x - d.x) * (b.y - c.y) - (c.y - d.y) * (b.x - c.x);
	if (s * t > 0) return false;

	return true;
}

double dis(D a, D b) { return std::hypot(a.first - b.first, a.second - b.second); }

lint fact[3000000];
void fact_init(lint n) {
	if (3000000 <= n) return;
	fact[0] = fact[1] = 1;
	for (lint i = 2; i <= n; ++i) {
		fact[i] = i * fact[i - 1];
		if (mod <= fact[i]) fact[i] %= mod;
	}
}

lint nck[1010][1010];
void comb_init(lint n) {
	if (1010 <= n) return;
	for (lint i = 0; i < n - 1; ++i) {
		for (lint j = 0; j < i + 1; ++j) {
			if (i == 0 || j == 0) nck[i][j] = 1;
			else nck[i][j] = nck[i - 1][j - 1] + nck[i - 1][j];
		}
	}
}

lint modpow(lint x, lint n, lint m) {
	lint res = 1;
	while (n > 0) {
		if (n & 1) {
			res *= x;
			if (m <= res) res %= m;
		}
		x *= x;
		if (m <= x) x %= m;
		n >>= 1;
	}
	return res;
}

lint comb(lint n, lint r) {
	if (r < 0 || n < r) return 0;
	if (r == 0 || r == n) return 1;
	lint res = fact[n] * modpow(fact[n - r], mod - 2, mod) % mod * modpow(fact[r], mod - 2, mod) % mod;
	return res < 0 ? res + mod : res;
}

map<lint, lint> factring(lint n) {
	map<lint, lint> res;
	for (lint i = 2; i * i <= n; ++i) {
		while (n % i == 0) {
			n /= i;
			++res[i];
		}
	}
	if (n != 1) ++res[n];
	return res;
}

class union_find {
private:

	vector<int>par, rank, size;

public:

	union_find(int n)
		:par(n), rank(n), size(n, 1)
	{
		std::iota(par.begin(), par.end(), 0);
	}

	int root(int x) {
		if (par[x] == x) return x;
		return par[x] = root(par[x]);
	}

	bool same(int x, int y) { return root(x) == root(y); }

	void unite(int x, int y) {
		x = root(x), y = root(y);
		if (x == y) return;
		if (rank[x] < rank[y]) par[x] = y, size[y] += size[x];
		else {
			par[y] = x;
			if (rank[x] == rank[y]) ++rank[x];
			size[x] += size[y];
		}
	}
};

template<class T>
class segment_tree {
private:

	int size;
	vector<T>dat;
	T unit;
	std::function<T(T, T)>func;

	T query_sub(int a, int b, int k, int l, int r) const {
		if (r <= a || b <= l) return unit;
		if (a <= l && r <= b) return dat[k];
		const T& vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
		const T& vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
		return func(vl, vr);
	}

public:

	segment_tree(int size_, const T& unit_, const std::function<T(T, T)>& func_)
		:unit(unit_), func(func_)
	{
		int x = 1;
		while (size_ > x) x <<= 1;
		size = x;
		dat.resize(2 * size - 1, unit);
	}

	void update(int i, const T& x) {
		i += size - 1;
		dat[i] = x;
		while (i > 0) {
			i = (i - 1) / 2;
			dat[i] = max(dat[i * 2 + 1], dat[i * 2 + 2]);
		}
	}

	T query(int a, int b) const { return query_sub(a, b, 0, 0, size); }

	const T& operator[](size_t index) const { return dat[index + size - 1]; }

	T& operator[](size_t index) { return dat[index + size - 1]; }
};

lint h, w, k, ans;
bool c[6][6];
int main() {
	cin >> h >> w >> k;
	rep(i, h) {
		rep(j, w) {
			char t; cin >> t;
			c[i][j] = (t == '#');
		}
	}
	rep(i, (1LL << h)) {
		rep(j, (1LL << w)) {
			bool tmp[6][6];
			lint cnt = 0;
			rep(i1, h) {
				rep(j1, w) {
					tmp[i1][j1] = c[i1][j1];
				}
			}
			rep(k1, h) {
				if (i & (1LL << k1)) {
					rep(l, w) {
						tmp[k1][l] = false;
					}
				}
			}
			rep(k1, w) {
				if (j & (1LL << k1)) {
					rep(l, h) {
						tmp[l][k1] = false;
					}
				}
			}
			rep(i1, h) {
				rep(j1, w) {
					cnt += tmp[i1][j1];
				}
			}
			if (cnt == k) ans++;
		}
	}
	cout << ans << endl;
}

Measured memory_bytes: 3716