#include "bits/stdc++.h"
using namespace std;
#define FOR(i,j,k) for(int (i)=(j);(i)<(int)(k);++(i))
#define rep(i,j) FOR(i,0,j)
#define each(x,y) for(auto &(x):(y))
#define mp make_pair
#define mt make_tuple
#define all(x) (x).begin(),(x).end()
#define debug(x) cout<<#x<<": "<<(x)<<endl
#define smax(x,y) (x)=max((x),(y))
#define smin(x,y) (x)=min((x),(y))
#define MEM(x,y) memset((x),(y),sizeof (x))
#define sz(x) (int)(x).size()
#define pb push_back
typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<ll> vll;

struct LazySegTreeSum{
    int dataSize;
    vector<long long> value, lazy;
    LazySegTreeSum(int n){
        dataSize = 1;
        while(dataSize < n)dataSize *= 2;
        int treeSize = 2 * dataSize;
        value = lazy = vector<long long>(treeSize);
    }

    void propagate(int index, int curL, int curR){
        // 自分の値をlazyにもとづいて書き換えて自分のlazyをクリア。
        if(lazy[index]){
            int left = index * 2, right = index * 2 + 1;
            value[index] += (curR - curL)*lazy[index];
            // 子のlazyを書き換える。
            if(curR - curL > 1){
                lazy[left] += lazy[index];
                lazy[right] += lazy[index];
            }
            lazy[index] = 0;
        }
    }

    // [curL, curR) を更新する
    void update(int index, int curL, int curR, int givenL, int givenR, int x){
        // 範囲外であろうとpropagateは必ず呼ぶ。そうでないと、親がうまく評価されない
        propagate(index, curL, curR);

        // 範囲外
        if(curR <= givenL || givenR <= curL)return;

        if(givenL <= curL && curR <= givenR){
            // 直接書き換えないでindexのlazyを書き換えてpropagateを呼ぶ
            lazy[index] += x;
            propagate(index, curL, curR);
        } else{
            int mid = (curL + curR) / 2;
            update(index * 2, curL, mid, givenL, givenR, x);
            update(index * 2 + 1, mid, curR, givenL, givenR, x);
            value[index] = value[index * 2] + value[index * 2 + 1];
        }
    }

    void update(int l, int r, int x){
        update(1, 0, dataSize, l, r, x);
    }

    long long query(int l, int r){
        return query(1, 0, dataSize, l, r);
    }

    long long query(int index, int curL, int curR, int givenL, int givenR){
        // 範囲外
        if(curR <= givenL || givenR <= curL)return 0;

        propagate(index, curL, curR);

        if(givenL <= curL && curR <= givenR){
            return value[index];
        } else{
            int mid = (curL + curR) / 2;
            ll resL = query(index * 2, curL, mid, givenL, givenR);
            ll resR = query(index * 2 + 1, mid, curR, givenL, givenR);
            return resL + resR;
        }
    }
};

const ll INF = LLONG_MAX / 2;
int N, M;
ll A[5010], B[5010][210];
stack<pair<ll, int>> st[5010];

int main(){
    cin >> N >> M;
    
    rep(i, N - 1) {
        scanf("%lld", &A[i + 1]);
        A[i + 1] += A[i];
    }
    rep(i, N)rep(j, M)scanf("%lld", &B[i][j]);
    rep(i, M)st[i].push(mp(INF, N));

    LazySegTreeSum sum(N + 1);

    ll ans = 0;
    for(int l = N - 1; l >= 0; --l) {
        rep(i, M) {
            ll x = B[l][i];
            while(x >= st[i].top().first) {
                int lb, ub;
                ll y;
                tie(y, lb) = st[i].top();
                st[i].pop();
                ub = st[i].top().second;
                sum.update(lb, ub, (int)(-y));
            }
            int r = st[i].top().second;
            sum.update(l, r, (int)x);
            st[i].push(mp(x, l));
        }
        FOR(i, l, N)smax(ans, sum.query(i, i + 1) - A[i] + A[l]);
    }
    cout << ans << endl;
}


Measured memory_bytes: 12160