Problem:
Score : 700 points

Problem Statement

Nuske has a grid with N rows and M columns of squares. The rows are numbered 1 through N from top to bottom, and the columns are numbered 1 through M from left to right.
Each square in the grid is painted in either blue or white. If S_{i,j} is 1, the square at the i-th row and j-th column is blue; if S_{i,j} is 0, the square is white.
For every pair of two blue square a and b, there is at most one path that starts from a, repeatedly proceeds to an adjacent (side by side) blue square and finally reaches b, without traversing the same square more than once.

Phantom Thnook, Nuske's eternal rival, gives Q queries to Nuske. The i-th query consists of four integers x_{i,1}, y_{i,1}, x_{i,2} and y_{i,2} and asks him the following: when the rectangular region of the grid bounded by (and including) the x_{i,1}-th row, x_{i,2}-th row, y_{i,1}-th column and y_{i,2}-th column is cut out, how many connected components consisting of blue squares there are in the region?

Process all the queries.

Constraints

1 ≤ N,M ≤ 2000

1 ≤ Q ≤ 200000

S_{i,j} is either 0 or 1.

S_{i,j} satisfies the condition explained in the statement.

1 ≤ x_{i,1} ≤ x_{i,2} ≤ N(1 ≤ i ≤ Q)

1 ≤ y_{i,1} ≤ y_{i,2} ≤ M(1 ≤ i ≤ Q)

Input

The input is given from Standard Input in the following format:

N M Q
S_{1,1}..S_{1,M}
:
S_{N,1}..S_{N,M}
x_{1,1} y_{i,1} x_{i,2} y_{i,2}
:
x_{Q,1} y_{Q,1} x_{Q,2} y_{Q,2}

Output

For each query, print the number of the connected components consisting of blue squares in the region.

Sample Input 1

3 4 4
1101
0110
1101
1 1 3 4
1 1 3 1
2 2 3 4
1 2 2 4

Sample Output 1

3
2
2
2

In the first query, the whole grid is specified. There are three components consisting of blue squares, and thus 3 should be printed.

In the second query, the region within the red frame is specified. There are two components consisting of blue squares, and thus 2 should be printed.
Note that squares that belong to the same component in the original grid may belong to different components.

Sample Input 2

5 5 6
11010
01110
10101
11101
01010
1 1 5 5
1 2 4 5
2 3 3 4
3 3 3 3
3 1 3 5
1 1 3 4

Sample Output 2

3
2
1
1
3
2

Solution in C++ (GCC 9.2.1) (judge verdict: Accepted)

/*
 * じょえチャンネル
 * 高評価・チャンネル登録よろしくおねがいします！
 * https://www.youtube.com/channel/UCRXsI3FL_kvaVL9zoolBfbQ
 */

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cfloat>
#include <climits>
#include <cmath>
#include <complex>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <memory>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <string>
#include <string.h>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

//#pragma GCC target("avx2")
//#pragma GCC optimize("O3")
//#pragma GCC optimize("unroll-loops")




//here!!!
// define int long long !!!!!

#define int long long

// define int long long !!!!!


#define mod 1000000007ll
//constexpr int mod = 998244353ll;

typedef long long ll;

#ifdef int
#define inf (int)(3e18)
#else
#define inf (int)(5e8)
#endif

#define intt long long
#define itn long long
#define P pair<long long,long long>

#define rep(i,n) for(int i=0;i<n;i++)
#define REP(i,n) for(int i=1;i<=n;i++)
#define rev_rep(i,n) for(int i=n-1;i>=0;i--)
#define REV_REP(i,n) for(int i=n;i>=1;i--)

#define ALL(v) v.begin(),v.end()

#define smallpriority_queue(x) priority_queue<x,vector<x>,greater<x>>

using namespace std;

//Library
//モッドパウ
inline int modpow(int x, int y, int m = mod) {
    int res = 1;
    while (y) {
        if (y & 1) {
            res *= x;
            res %= m;
        }
        x = x * x % m;
        y /= 2;
    }
    return res;
}

int mypow(int x, int y) {
    int res = 1;
    while (y) {
        if (y % 2) {
            res *= x;
        }
        x = x * x;
        y /= 2;
    }
    return res;
}
//is the number (x) a prime number?
bool prime(int x) {
    if (!x || x == 1) {
        return false;
    }
    for (int i = 2; i * i <= x; i++) {
        if (!(x % i)) {
            return false;
        }
    }
    return true;
}


//saidai-kouyakusuu
inline int gcd(int x, int y) {
    if (!y) {
        return x;
    }
    return gcd(y, x % y);
}


//number of keta
int keta(int x) {
    int ans = 0;
    while (x) {
        x /= 10;
        ans++;
    }
    return ans;
}

//number of 2shinsuu keta
int bitketa(int x) {
    int ans = 0;
    while (x) {
        x >>= 1;
        ans++;
    }
    return ans;
}

//sum of keta
int ketasum(int x) {
    int ans = 0;
    while (x) {
        ans += x % 10;
        x /= 10;
    }
    return ans;
}

inline int lcm(int x, int y) {
    int ans = x / gcd(x, y) * y;
    return ans;
}
int twobeki(int x) {
    int ans = 0;
    while (1) {
        if (!(x & 1)) {
            ans++;
            x >>= 1;
        }
        else {
            break;
        }
    }
    return ans;
}


template <class T, class U>
inline bool chmax(T& lhs, const U& rhs) {
    if (lhs < rhs) {
        lhs = rhs;
        return 1;
    }
    return 0;
}
template <class T, class U>
inline bool chmin(T& lhs, const U& rhs) {
    if (lhs > rhs) {
        lhs = rhs;
        return 1;
    }
    return 0;
}
void Yes(){
    cout<<"Yes"<<endl;
}
void No(){
    cout<<"No"<<endl;
}
void YES(){
    cout<<"YES"<<endl;
}
void NO(){
    cout<<"NO"<<endl;
}


#define fin(i) scanf("%lld",&i)
#define fout(i) printf("%lld",i)
#define fendl printf("\n")

int kai(int x, int y) {
    int res = 1;
    for (int i = x - y + 1; i <= x; i++) {
        res *= i; res %= mod;
    }
    return res;
}

int comb(int x, int y) {
    if (y > x)return 0;
    //    cout<<kai(x, y)<<' '<<modpow(kai(y, y), mod - 2)<<endl;
    return kai(x, y) * modpow(kai(y, y), mod - 2) % mod;
}
//Library-End


#define vecin(v) for(int i=0;i<v.size();i++)scanf("%lld",&v[i]);
#define vecout(v) {for(int i=0;i<(int)v.size();i++)printf("%lld ",v[i]);printf("\n");}


template<typename T>
class kaageSegTree {
protected:
    unsigned int n = 1, rank = 0;
    std::vector<T> node;
    T nodee;
    virtual T nodef(const T&, const T&)const = 0;
public:
    kaageSegTree(unsigned int m, T init, T nodee):nodee(nodee) {
        while (n < m) {
            n *= 2;
            rank++;
        }
        node.resize(2 * n);
        for (unsigned int i = n; i < 2 * n; i++)node[i] = init;
    }
    kaageSegTree(const std::vector<T>& initvec, T nodee):nodee(nodee) {
        unsigned int m = initvec.size();
        while (n < m) {
            n *= 2;
            rank++;
        }
        node.resize(2 * n);
        for (unsigned int i = n; i < 2 * n; i++) {
            if (i - n < m)node[i] = initvec[i - n];
        }
    }
    virtual void update(int i, T x) {
        i += n;
        node[i] = x;
        while (i != 1) {
            i >>= 1;
            node[i] = nodef(node[2 * i], node[2 * i + 1]);
        }
    }
    virtual T query(int l, int r) {
        l += n; r += n;
        T ls = nodee, rs = nodee;
        while (l < r) {
            if (l & 1) {
                ls = nodef(ls, node[l]);
                l++;
            }
            if (r & 1) {
                r--;
                rs = nodef(node[r], rs);
            }
            l >>= 1; r >>= 1;
        }
        return nodef(ls, rs);
    }
    virtual T operator[](const int& x) {
        return node[n + x];
    }
    void fill(T x) {
        std::fill(ALL(node), x);
    }
    void print() {
        rep(i, n)std::cout << operator[](i) << " ";
        std::cout << std::endl;
    }
};
class RSQ :public kaageSegTree<int> {
    int nodef(const int& lhs,const int& rhs)const{return lhs+rhs;}
public:
    RSQ(int size, const int& def = 0) :kaageSegTree<int>(size, def, 0) {}
    RSQ(const std::vector<int>& initvec) :kaageSegTree<int>(initvec, 0) {}
};
class RMiQ :public kaageSegTree<int> {
    int nodef(const int& lhs,const int& rhs)const{return std::min(lhs,rhs);}
public:
    RMiQ(int size, const int& def = 0) :kaageSegTree<int>(size, def, inf) {}
    RMiQ(const std::vector<int>& initvec) :kaageSegTree<int>(initvec, inf) {}
};
class RMaQ :public kaageSegTree<int> {
    int nodef(const int& lhs,const int& rhs)const{return std::max(lhs,rhs);}
public:
    RMaQ(int size, const int& def = 0) :kaageSegTree<int>(size, def, -inf) {}
    RMaQ(const std::vector<int>& initvec) :kaageSegTree<int>(initvec, -inf) {}
};
template<typename T, typename U>
class IntervalSegTree :public kaageSegTree<T> {
protected:
    using kaageSegTree<T>::n;
    using kaageSegTree<T>::rank;
    using kaageSegTree<T>::node;
    using kaageSegTree<T>::nodef;
    using kaageSegTree<T>::nodee;
    std::vector<U> lazy;
    std::vector<bool> lazyflag;
    std::vector<int> width;
    virtual void lazyf(U&, const U&) = 0;
    virtual void updf(T&, const U&, const unsigned int&) = 0;
    void eval(int k) {
        for (int i = rank; i > 0; i--) {
            int nk = k >> i;
            if (lazyflag[nk]) {
                updf(node[2 * nk], lazy[nk], width[2 * nk]);
                updf(node[2 * nk + 1], lazy[nk], width[2 * nk + 1]);
                if (lazyflag[2 * nk])lazyf(lazy[2 * nk], lazy[nk]);
                else lazy[2 * nk] = lazy[nk];
                if (lazyflag[2 * nk + 1])lazyf(lazy[2 * nk + 1], lazy[nk]);
                else lazy[2 * nk + 1] = lazy[nk];
                lazyflag[2 * nk] = lazyflag[2 * nk + 1] = true;
                lazyflag[nk] = false;
            }
        }
    }
public:
    IntervalSegTree(unsigned int m, T init, T nodee) :kaageSegTree<T>(m, init, nodee) {
        lazy.resize(2 * n); lazyflag.resize(2 * n); width.resize(2 * n);
        width[1] = n;
        for (unsigned int i = 2; i < 2 * n; i++) {
            width[i] = width[i >> 1] >> 1;
        }
    }
    IntervalSegTree(T nodee, const std::vector<T>& initvec) :kaageSegTree<T>(initvec, nodee) {
        lazy.resize(2 * n); lazyflag.resize(2 * n); width.resize(2 * n);
        width[1] = n;
        for (unsigned int i = 2; i < 2 * n; i++) {
            width[i] = width[i >> 1] >> 1;
        }
    }
    void update(int i, U x) {
        i += n;
        eval(i);
        updf(node[i], x, width[i]);
        if (lazyflag[i])lazyf(lazy[i], x);
        else {
            lazyflag[i] = true;
            lazy[i] = x;
        }
        while (i /= 2)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    void update(int l, int r, U x) {
        l += n; r += n;
        int nl = l, nr = r;
        while (!(nl & 1))nl >>= 1;
        while (!(nr & 1))nr >>= 1;
        nr--;
        eval(nl); eval(nr);
        while (l < r) {
            if (l & 1) {
                updf(node[l], x, width[l]);
                if (lazyflag[l])lazyf(lazy[l], x);
                else {
                    lazyflag[l] = true;
                    lazy[l] = x;
                }
                l++;
            }
            if (r & 1) {
                r--;
                updf(node[r], x, width[r]);
                if (lazyflag[r])lazyf(lazy[r], x);
                else {
                    lazyflag[r] = true;
                    lazy[r] = x;
                }
            }
            l >>= 1; r >>= 1;
        }
        while (nl >>= 1)node[nl] = nodef(node[2 * nl], node[2 * nl + 1]);
        while (nr >>= 1)node[nr] = nodef(node[2 * nr], node[2 * nr + 1]);
    }
    T query(int l, int r) {
        l += n; r += n;
        eval(l); eval(r - 1);
        int ls = nodee, rs = nodee;
        while (l < r) {
            if (l & 1) {
                ls = nodef(ls, node[l]);
                l++;
            }
            if (r & 1) {
                r--;
                rs = nodef(node[r], rs);
            }
            l >>= 1; r >>= 1;
        }
        return nodef(ls, rs);
    }
    T operator[](const int& x) {
        eval(n + x);
        return node[n + x];
    }
    T queryForAll() {
        return node[1];
    }
};
class RAQRSQ :public IntervalSegTree<int, int> {
    int nodef(const int& a, const int& b)const { return a + b; }
    void lazyf(int& a, const int& b) { a += b; }
    void updf(int& a, const int& b, const unsigned int& width) { a += width * b; }
public:
    RAQRSQ(int size, const int& def = 0) :IntervalSegTree<int, int>(size, def, 0) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    RAQRSQ(const std::vector<int>& initvec) :IntervalSegTree<int, int>((int)0, initvec) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
};
class RAQRMiQ :public IntervalSegTree<int, int> {
    int nodef(const int& a, const int& b)const { return std::min(a, b); }
    void lazyf(int& a, const int& b) { a += b; }
    void updf(int& a, const int& b, const unsigned int& width) { a += b; }
public:
    RAQRMiQ(int size, const int& def = 0) :IntervalSegTree<int, int>(size, def, inf) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    RAQRMiQ(const std::vector<int>& initvec) :IntervalSegTree<int, int>(inf, initvec) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
};
class RAQRMaQ :public IntervalSegTree<int, int> {
    int nodef(const int& a, const int& b)const { return std::max(a, b); }
    void lazyf(int& a, const int& b) { a += b; }
    void updf(int& a, const int& b, const unsigned int& width) { a += b; }
public:
    RAQRMaQ(unsigned int size, const int& def = 0) :IntervalSegTree<int, int>(size, def, -inf) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    RAQRMaQ(const std::vector<int>& initvec) :IntervalSegTree<int, int>(-inf, initvec) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
};
class RUQRSQ :public IntervalSegTree<int, int> {
    int nodef(const int& a, const int& b)const { return a + b; }
    void lazyf(int& a, const int& b) { a = b; }
    void updf(int& a, const int& b, const unsigned int& width) { a = width * b; }
public:
    RUQRSQ(int size, const int& def = 0) :IntervalSegTree<int, int>(size, def, 0) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    RUQRSQ(const std::vector<int>& initvec) :IntervalSegTree<int, int>((int)0, initvec) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
};
class RUQRMiQ :public IntervalSegTree<int, int> {
    int nodef(const int& a, const int& b)const { return std::min(a, b); }
    void lazyf(int& a, const int& b) { a = b; }
    void updf(int& a, const int& b, const unsigned int& width) { a = b; }
public:
    RUQRMiQ(int size, const int& def = 0) :IntervalSegTree<int, int>(size, def, inf) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    RUQRMiQ(const std::vector<int>& initvec) :IntervalSegTree<int, int>(inf, initvec) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
};
class RUQRMaQ :public IntervalSegTree<int, int> {
    int nodef(const int& a, const int& b)const { return std::max(a, b); }
    void lazyf(int& a, const int& b) { a = b; }
    void updf(int& a, const int& b, const unsigned int& width) { a = b; }
public:
    RUQRMaQ(int size, const int& def = 0) :IntervalSegTree<int, int>(size, def, -inf) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
    RUQRMaQ(const std::vector<int>& initvec) :IntervalSegTree<int, int>(-inf, initvec) {
        for (int i = n - 1; i > 0; i--)node[i] = nodef(node[2 * i], node[2 * i + 1]);
    }
};

////SegTree
//template <class T>
//class SegTree {
//    int n;                       // 葉の数
//    vector<T> node;              // データを格納するvector
//    T def;                       // 初期値かつ単位元
//    function<T(T, T)> operation; // 区間クエリで使う処理
//    function<T(T, T)> update;    // 点更新で使う処理
//
//    // 区間[a,b)の総和。ノードk=[l,r)に着目している。
//    T _query(int a, int b, int k, int l, int r) {
//        if (r <= a || b <= l) return def; // 交差しない
//        if (a <= l && r <= b)
//            return node[k]; // a,l,r,bの順で完全に含まれる
//        else {
//            T c1 = _query(a, b, 2 * k + 1, l, (l + r) / 2); // 左の子
//            T c2 = _query(a, b, 2 * k + 2, (l + r) / 2, r); // 右の子
//            return operation(c1, c2);
//        }
//    }
//
//public:
//    // _n:必要サイズ, _def:初期値かつ単位元, _operation:クエリ関数,
//    // _update:更新関数
//    SegTree(size_t _n, T _def, function<T(T, T)> _operation,
//            function<T(T, T)> _update)
//    : def(_def), operation(_operation), update(_update) {
//        n = 1;
//        while (n < _n) {
//            n *= 2;
//        }
//        node = vector<T>(2 * n , def);
//    }
//
//    // 場所i(0-indexed)の値をxで更新
//    void change(int i, T x) {
//        i += n - 1;
//        node[i] = update(node[i], x);
//        while (i > 0) {
//            i = (i - 1) / 2;
//            node[i] = operation(node[i * 2 + 1], node[i * 2 + 2]);
//        }
//    }
//
//    // [a, b)の区間クエリを実行
//    T query(int a, int b) {
//        return _query(a, b, 0, 0, n);
//    }
//
//    // 添字でアクセス
//    T operator[](int i) {
//        return node[i + n - 1];
//    }
//};

template <class T>
class SegTree {
    int n;
    vector<T> node;
    T def;
    function<T(T,T)> operation;
    function<T(T,T)> update;
    
public:
    SegTree(unsigned int _n, T _def, function<T(T, T)> _operation,
            function<T(T, T)> _update)
    : def(_def), operation(_operation), update(_update) {
        n=1;
        while (n < _n) {
            n *= 2;
        }
        node = vector<T>(n * 2, def);
    }
    SegTree(vector<int>& initvec, function<T(T, T)> _operation,
            function<T(T, T)> _update)
    :  operation(_operation), update(_update) {
        n=1;
        while (n < initvec.size()) {
            n *= 2;
        }
        node = vector<T>(n * 2, def);
        for(int i=n;i<n+initvec.size();i++){
            node[i]=initvec[i-n];
        }
        for(int i=n-1;i>=1;i--){
            node[i]=operation(node[i*2],node[i*2+1]);
        }
    }
    void change(int i,T x){
        i+=n;
        node[i]=update(node[i],x);
        while (i>=1) {
            i>>=1;
            node[i]=operation(node[i*2],node[i*2+1]);
        }
    }
    T query(int l, int r){
        l+=n;
        r+=n;
        T rx=def,lx=def;
        while(l<r){
            if (l&1) {
                lx=operation(lx,node[l]);
                l++;
            }
            if (r&1) {
                r--;
                rx=operation(node[r],rx);
            }
            l>>=1; r>>=1;
        }
        return operation(lx,rx);
    }
    T operator [] (const int& x){
        return node[x+n];
    }
    void fill(T x) {
        std::fill(ALL(node), x);
    }
    void print() {
        rep(i, n)std::cout << operator[](i) << " ";
        std::cout << std::endl;
    }
    
};

#define R_MIN ([](long long a, long long b) { return min(a, b); })
#define R_MAX ([](long long a, long long b) { return max(a, b); })
#define R_SUM ([](long long a, long long b) { return a + b; })

#define NORMAL_UPDATE ([](long long a, long long b) { return b; })
#define ADD_UPDATE ([](long long a, long long b) { return a + b; })
#define MINUS_UPDATE ([](long long a, long long b) { return a - b; }

class Union_Find {
    vector<int> par;
    vector<int> rankmy;
    vector<int> ookisa;
    
public:
    Union_Find(int size) {
        par = vector<int>(size);
        rankmy = vector<int>(size);
        ookisa=vector<int>(size);
        for (int i = 0; i < size; i++) {
            par[i] = i;
            ookisa[i]=1;
        }
    }
    
    int find(int x) {
        if (par[x] == x) {
            return x;
        }
        return par[x] = find(par[x]);
    }
    
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return;
        }
        if (rankmy[x] < rankmy[y]) {
            par[x] = y;
            ookisa[y]+=ookisa[x];
            ookisa[x]=0;
        }
        else {
            par[y] = x;
            ookisa[x]+=ookisa[y];
            ookisa[y]=0;
            if (rankmy[x] == rankmy[y]) {
                rankmy[x]++;
            }
        }
    }
    int size(int i){
        i=find(i);
        return ookisa[i];
    }
    bool same(int x, int y){
        return find(x) == find(y);
    }
    
};

class BIT {
    vector<int> data;
    int size=0;
public:
    BIT(int _size){
        data=vector<int>(_size+1);
        size=_size;
    }
    void add(int i,int x){
        while (i<=size) {
            data[i]+=x;
            i += i & -i;
        }
    }
    int sum(int i){
        assert(i<=size);
        int s=0;
        while(i>0){
            s+=data[i];
            i -= i & -i;
        }
        return s;
    }
    int lower_bound(int x){
        if(x<=0){
            return 0;
        }else{
            int i=0;int r=1;
            while(r<size) r=r<<1;
            for(int len=r;len>0;len=len>>1) {
                if(i+len<size && data[i+len]<x){
                    x-=data[i+len];
                    i+=len;
                }
            }
            return i+1;
        }
    }
};

//Union-Find-End

int perm[2000005];
void init_perm() {
    perm[0] = 1;
    REP(i, 2000004) {
        perm[i] = perm[i - 1] * i % mod;
    }
}

int nCk(int x, int y) {
    if (y>x) {
        return 0;
    }
    if (x<0) {
        return 0;
    }
    return perm[x] * modpow(perm[x - y], mod - 2) % mod * modpow(perm[y], mod - 2) % mod;
}

double kyori(pair<int, int> f, pair<int, int> s) {
    double ans = 0;
    double t = fabs(f.first - s.first);
    double y = fabs(f.second - s.second);
    ans = sqrt(t * t + y * y);
    return ans;
}

inline string stringmax(string& x,string& y){
    if (x.size()>y.size()) {
        return x;
    }
    if (x.size()<y.size()) {
        return y;
    }
    rep(i,x.size()){
        if (x[i]>y[i]) {
            return x;
        }
        if (x[i]<y[i]) {
            return y;
        }
    }
    return x;
}

vector<int>  RollingHash(string &s, string& t){
    vector<int> ans;
    __int128 h=0,hamod=0,ki=0,kim=0,hikaku=0,one=0;
    one=1;
    ki=1000000007ll;
    hamod=(one<<61)-1;
    kim=1;
    rep(i,t.size()){
        hikaku*=ki;
        h*=ki;
        kim*=ki;
        hikaku+=t[i];
        h+=s[i];
        hikaku%=hamod;
        h%=hamod;
        kim%=hamod;
    }
    rep(i,(int)s.size()-(int)t.size()+1){
        if (h==hikaku) {
            ans.emplace_back(i);
        }
        h*=ki;
        h%=hamod;
        h+=s[i+(int)t.size()];
        h%=hamod;
        h+=hamod;
        h-=s[i]*kim%hamod;
        h%=hamod;
    }
    return ans;
}
struct edge {
    int to;
    int length;
    edge(int _to, int _length){
        to=_to;
        length=_length;
    }
};
vector<int> djkstra(vector<vector<edge>> &road,int start){
    vector<int> kyo(road.size(),inf);
    smallpriority_queue(P) q;
    q.push({0,start});
    kyo[start]=0;
    while (q.size()) {
        int x=q.top().second;
        itn now=q.top().first;
        q.pop();
        if (kyo[x]<now) {
            continue;
        }
        for(auto&i:road[x]){
            if (kyo[i.to]>now+i.length) {
                kyo[i.to]=now+i.length;
                q.push({kyo[i.to],i.to});
            }
        }
    }
    return kyo;
}

template <class T>
void change_to_unique(vector<T> &v){
    std::sort(ALL(v));
    auto k=unique(ALL(v));
    if(k!=v.end())v.erase(k,v.end());
}

#define endl "\n" //interactive の時に注意！！！
#define Endl "\n" //interactive の時に注意！！！
#define printd cout<<fixed<<setprecision(10)
#define rrep(i,f,s) for(int i=f;i<s;i++)
#define RREP(i,f,s) for(int i=f;i<=s;i++)


int n,m,q;
itn x1,x2,yy1,y2;
int rui[2004][2004],kasane[2004][2004],cntx[2004][2004],cnty[2004][2004];
string s[2004];
signed main() {
    ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    cin>>n>>m>>q;
    REP(i,n){
        cin>>s[i];
        rep(j,m){
            rui[i][j+1]+=s[i][j]-'0';
        }
    }
    REP(i,n){
        REP(j,m){
            rui[i][j]+=rui[i][j-1];
        }
        REP(j,m){
            rui[i][j]+=rui[i-1][j];
        }
    }
    REP(i,n){
        rep(j,m){
            if (s[i][j]=='1') {
                if(i<n){
                    if (s[i+1][j]=='1') {
                        kasane[i][j+1]++;
                        cntx[i][j+1]++;
                    }
                }
                if(j<m-1){
                    if (s[i][j+1]=='1') {
                        kasane[i][j+1]++;
                        cnty[j+1][i]++;
                    }
                }
            }
        }
    }
    REP(i,n){
        REP(j,m){
            cntx[i][j]+=cntx[i][j-1];
        }
    }
    REP(j,n){
        REP(i,m){
            cnty[i][j]+=cnty[i][j-1];
        }
    }
    REP(i,n){
        REP(j,m){
            kasane[i][j]+=kasane[i][j-1];
        }
        REP(j,m){
            kasane[i][j]+=kasane[i-1][j];
        }
    }
    rep(i,q){
        cin>>x1>>yy1>>x2>>y2;
        int kosuu=rui[x2][y2]-rui[x2][yy1-1]-rui[x1-1][y2]+rui[x1-1][yy1-1];
        int cnt=kasane[x2][y2]-kasane[x2][yy1-1]-kasane[x1-1][y2]+kasane[x1-1][yy1-1];
        cnt-=cntx[x2][y2]-cntx[x2][yy1-1];
        cnt-=cnty[y2][x2]-cnty[y2][x1-1];
        cout<<kosuu-cnt<<endl;
    }
}