#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <unordered_map>
#include <vector>

using namespace std;

typedef long long lint;

template<typename T>
struct split_multiset_t {
  multiset<T> low, high;
  int split_size = 0;
  function<void(T)> low_insert_cb, low_erase_cb;
  split_multiset_t() {}
  void insert(const T &value) {
    if (low.empty() || value >= *low.rbegin()) {
      high.insert(value);
    } else {
      _low_insert(value);
    }
    refresh_split();
  }
  bool erase(const T &value) {
    auto it = high.find(value);
    if (it != high.end()) {
      high.erase(it);
    } else {
      it = low.find(value);
      if (it == low.end())
        return false;
      _low_erase(it);
    }
    refresh_split();
    return true;
  }
  void refresh_split() {
    while (!low.empty() && (int)low.size() > split_size) {
      auto it = prev(low.end());
      T value = *it;
      high.insert(value);
      _low_erase(it);
    }
    while (!high.empty() && (int)low.size() < split_size) {
      auto it = high.begin();
      T value = *it;
      _low_insert(value);
      high.erase(it);
    }
  }
  void set_split_size(int new_split_size) {
    split_size = new_split_size;
    refresh_split();
  }
  void clear() {
    for (const T &value : low)
      if (low_erase_cb)
        low_erase_cb(value);
    low.clear();
    high.clear();
  }
  int size() {
    return low.size() + high.size();
  }

  void _low_insert(const T &value) {
    low.insert(value);
    if (low_insert_cb) low_insert_cb(value);
  }
  void _low_erase(const multiset<int>::iterator &it) {
    if (low_erase_cb) low_erase_cb(*it);
    low.erase(it);
  }
};

struct unionfind_t {
  int n;
  vector<int> parent, rank;
  unionfind_t(int _n): n(_n), parent(n), rank(n, 0) {
    iota(parent.begin(), parent.end(), 0);
  }
  int find(int v) {
    if (parent[v] == v) return v;
    return parent[v] = find(parent[v]);
  }
  int unite(int a, int b) {
    a = find(a);
    b = find(b);
    if (a == b) return a;
    if (rank[a] > rank[b]) swap(a, b);
    parent[a] = b;
    if (rank[a] == rank[b]) ++rank[b];
    return b;
  }
};

template<typename T>
void merge(split_multiset_t<T> *a, split_multiset_t<T> *b, int k) {
  if (a->size() < b->size()) {
    merge(b, a, k);
    swap(*a, *b);
    return;
  }
  for (const T &v : b->low) a->insert(v);
  for (const T &v : b->high) a->insert(v);
  b->clear();
}

int main() {
  int n, k, q;
  scanf("%d %d %d", &n, &k, &q);
  vector<int> values(n);
  for (int i = 0; i < n; ++i)
    scanf("%d", &values[i]);
  vector<pair<int, int>> values_and_pos(n);
  for (int i = 0; i < n; ++i)
    values_and_pos[i] = make_pair(values[i], i);
  sort(values_and_pos.begin(), values_and_pos.end(), greater<pair<int, int>>());

  int result = INT_MAX;
  unionfind_t uf(n);
  split_multiset_t<int> useful_values;
  useful_values.set_split_size(q);
  vector<split_multiset_t<int>> sets(n);
  for (const pair<int, int> &value_and_pos : values_and_pos) {
    int value, pos;
    tie(value, pos) = value_and_pos;
    split_multiset_t<int> group;
    group.low_insert_cb = [&](int v) { useful_values.insert(v); };
    group.low_erase_cb = [&](int v) { useful_values.erase(v); };
    group.insert(value);
    int repr = pos;
    for (int adj : {pos - 1, pos + 1})
      if (0 <= adj && adj < n && make_pair(values[adj], adj) > value_and_pos) {
        merge(&group, &sets[uf.find(adj)], k);
        repr = uf.unite(repr, adj);
      }
    group.set_split_size(max((int)group.size() - (k - 1), 0));
    sets[repr] = move(group);
    if ((int)useful_values.low.size() == q)
      result = min(result, *useful_values.low.rbegin() - value);
  }
  printf("%d\n", result);
  return 0;
}


Measured memory_bytes: 896