#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> ii;
const int INF = 1e9;
const ll LINF = 1e18;
template<typename T>
void maximize(T& a, const T& b) {
if (a < b) a = b;
}
const int N = 1e3 + 5;
// Đây không hẳn là một bài cơ bản như trong đề bài nhắc đến
// Nếu so với bài QBRECT - tìm hình chữ nhật lớn nhất thì có thể nói các bài toán đếm đều khó chịu hơn so với các bài toán tìm min/max
// Tương tự ở bài này ta cũng sẽ giải quyết bài toán theo từng hàng
// Khi xét đến hàng thứ i cần tính được các thông tin:
// h[j] = độ cao của cột thứ j = độ dài đoạn con liên tiếp dài nhất kết thúc tại a(i, j) và chỉ chứa toàn số 1
// l[j] = phần tử gần nhất bên trái < h[j]
// Gọi dp[j] = Số hình chữ nhật thoả mãn chỉ chứa toàn số 1 và có ô góc phải dưới là ô a(i, j)
// Để tính dp[j] ta cũng sẽ xét 2 trường hợp:
// Trường hợp 1: cột j đóng vai trò là min (cột nhỏ nhất)
// - Chọn chiều cao của cột thứ j: 1 -> h[j]
// - Chọn các cột ở trước để lấp vào: l[j] + 1 -> j
// => Tổng số cách = h[j] * (j - l[j])
// Trường hợp 2: cột j không đóng vai trò là min
// Chiều cao các cột từ l[j] + 1 đến j sẽ phụ thuộc vào chiều cao của cột ở trước đó đóng vai trò là min
// => dp[l[j]]
int n, m;
int a[N][N];
int h[N];
int l[N];
ll dp[N];
ll solve(int i) {
for (int j = 1; j <= m; j++) {
if (a[i][j] == 0) h[j] = 0;
else h[j]++;
}
vector<int> st;
for (int j = 1; j <= m; j++) {
while (!st.empty() && h[st.back()] >= h[j]) st.pop_back();
l[j] = st.empty() ? 0 : st.back();
st.push_back(j);
}
for (int j = 1; j <= m; j++) {
dp[j] = h[j] * (j - l[j]); // Trường hợp 1
dp[j] += dp[l[j]]; // Trường hợp 2
}
ll ans = 0;
for (int j = 1; j <= m; j++) ans += dp[j];
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
string s; cin >> s;
s = ' ' + s;
for (int j = 1; j <= m; j++) {
a[i][j] = s[j] - '0';
}
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
ans += solve(i);
}
cout << ans << '\n';
}