#include <bits/stdc++.h>
using namespace std;
const long long LL_INF = (long long)2e18 + 5;
struct segment_change {
  long long to_add;
  segment_change(long long _to_add = 0) : to_add(_to_add) {}
  void reset() { to_add = 0; }
  bool has_change() const { return to_add != 0; }
  segment_change combine(const segment_change &other) const {
    return segment_change(to_add + other.to_add);
  }
};
struct segment {
  long long maximum;
  segment(long long _maximum = -LL_INF) : maximum(_maximum) {}
  void apply(int, const segment_change &change) { maximum += change.to_add; }
  void join(const segment &other) { maximum = max(maximum, other.maximum); }
  void join(const segment &a, const segment &b) {
    *this = a;
    join(b);
  }
};
pair<int, int> right_half[32];
struct add_max_seg_tree {
  int tree_n = 0;
  vector<segment> tree;
  vector<segment_change> changes;
  add_max_seg_tree(int n = -1) {
    if (n >= 0) init(n);
  }
  void init(int n) {
    tree_n = 1;
    while (tree_n < n) tree_n *= 2;
    tree.assign(2 * tree_n, segment());
    changes.assign(tree_n, segment_change());
  }
  void build(const vector<segment> &initial) {
    int n = initial.size();
    init(n);
    assert(n <= tree_n);
    for (int i = 0; i < n; i++) tree[tree_n + i] = initial[i];
    for (int position = tree_n - 1; position > 0; position--)
      tree[position].join(tree[2 * position], tree[2 * position + 1]);
  }
  void push_down(int position, int length) {
    if (!changes[position].has_change()) return;
    if (2 * position < tree_n) {
      changes[2 * position] = changes[2 * position].combine(changes[position]);
      changes[2 * position + 1] =
          changes[2 * position + 1].combine(changes[position]);
    }
    tree[2 * position].apply(length / 2, changes[position]);
    tree[2 * position + 1].apply(length / 2, changes[position]);
    changes[position].reset();
  }
  void push_all(int a, int b) {
    assert(0 <= a && a < b && b <= tree_n);
    a += tree_n;
    b += tree_n - 1;
    for (int up = 31 - __builtin_clz(tree_n); up > 0; up--) {
      int x = a >> up, y = b >> up;
      push_down(x, 1 << up);
      if (x != y) push_down(y, 1 << up);
    }
  }
  void join_and_apply(int position, int length) {
    tree[position].join(tree[2 * position], tree[2 * position + 1]);
    tree[position].apply(length, changes[position]);
  }
  void join_all(int a, int b) {
    assert(0 <= a && a < b && b <= tree_n);
    a += tree_n;
    b += tree_n - 1;
    int length = 1;
    while (a > 1) {
      a /= 2;
      b /= 2;
      length *= 2;
      join_and_apply(a, length);
      if (a != b) join_and_apply(b, length);
    }
  }
  template <typename T_range_op>
  void process_range(int a, int b, bool needs_join, T_range_op &&range_op) {
    if (a == b) return;
    push_all(a, b);
    int original_a = a, original_b = b;
    int length = 1, r_size = 0;
    for (a += tree_n, b += tree_n; a < b; a /= 2, b /= 2, length *= 2) {
      if (a & 1) range_op(a++, length);
      if (b & 1) right_half[r_size++] = {--b, length};
    }
    for (int i = r_size - 1; i >= 0; i--)
      range_op(right_half[i].first, right_half[i].second);
    if (needs_join) join_all(original_a, original_b);
  }
  segment query(int a, int b) {
    assert(0 <= a && a <= b && b <= tree_n);
    segment answer;
    process_range(a, b, false,
                  [&](int position, int) { answer.join(tree[position]); });
    return answer;
  }
  void update(int a, int b, const segment_change &change) {
    assert(0 <= a && a <= b && b <= tree_n);
    process_range(a, b, true, [&](int position, int length) {
      tree[position].apply(length, change);
      if (position < tree_n)
        changes[position] = changes[position].combine(change);
    });
  }
  void set_value(int index, long long value) {
    assert(0 <= index && index < tree_n);
    long long current = query(index, index + 1).maximum;
    update(index, index + 1, segment_change(value - current));
  }
};
int N, M, K;
vector<long long> transition(vector<long long> dp, vector<int> row,
                             vector<int> next_row, bool first = false) {
  vector<long long> partial_sums(M + 1, 0);
  vector<long long> next_sums(M + 1, 0);
  for (int i = 0; i < M; i++) {
    partial_sums[i + 1] = partial_sums[i] + row[i];
    next_sums[i + 1] = next_sums[i] + next_row[i];
  }
  auto &&get_both_sum = [&](int start, int end) {
    return partial_sums[end] - partial_sums[start] + next_sums[end] -
           next_sums[start];
  };
  if (first) {
    for (int i = 0; i <= M - K; i++) dp[i] = get_both_sum(i, i + K);
    return dp;
  }
  add_max_seg_tree tree(M - K + 1);
  vector<segment> initial(M - K + 1);
  for (int i = 0; i <= M - K; i++) {
    int start = i, end = K;
    long long value = dp[i];
    if (start < end) value -= partial_sums[end] - partial_sums[start];
    initial[i].maximum = value;
  }
  tree.build(initial);
  vector<long long> next_dp(M - K + 1, -LL_INF);
  for (int i = 0; i <= M - K; i++) {
    next_dp[i] = get_both_sum(i, i + K) + tree.tree[1].maximum;
    tree.update(max(i - (K - 1), 0), i + 1, segment_change(row[i]));
    if (i + K < M)
      tree.update(i + 1, min(i + K + 1, M - K + 1),
                  segment_change(-row[i + K]));
  }
  return next_dp;
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cin >> N >> M >> K;
  vector<vector<int>> A(N, vector<int>(M));
  for (int i = 0; i < N; i++)
    for (int j = 0; j < M; j++) cin >> A[i][j];
  A.push_back(vector<int>(M, 0));
  vector<long long> dp(M - K + 1, 0);
  dp = transition(dp, A[0], A[1], true);
  for (int i = 1; i < N; i++) dp = transition(dp, A[i], A[i + 1]);
  cout << *max_element(dp.begin(), dp.end()) << '\n';
}
