#include #include using namespace std; using int64 = long long; const int64 infll = (1LL << 62) - 1; const int inf = (1 << 30) - 1; struct IoSetup { IoSetup() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(10); cerr << fixed << setprecision(10); } } iosetup; template< typename T1, typename T2 > ostream &operator<<(ostream &os, const pair< T1, T2 >& p) { os << p.first << " " << p.second; return os; } template< typename T1, typename T2 > istream &operator>>(istream &is, pair< T1, T2 > &p) { is >> p.first >> p.second; return is; } template< typename T > ostream &operator<<(ostream &os, const vector< T > &v) { for(int i = 0; i < (int) v.size(); i++) { os << v[i] << (i + 1 != v.size() ? " " : ""); } return os; } template< typename T > istream &operator>>(istream &is, vector< T > &v) { for(T &in : v) is >> in; return is; } template< typename T1, typename T2 > inline bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); } template< typename T1, typename T2 > inline bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); } template< typename T = int64 > vector< T > make_v(size_t a) { return vector< T >(a); } template< typename T, typename... Ts > auto make_v(size_t a, Ts... ts) { return vector< decltype(make_v< T >(ts...)) >(a, make_v< T >(ts...)); } template< typename T, typename V > typename enable_if< is_class< T >::value == 0 >::type fill_v(T &t, const V &v) { t = v; } template< typename T, typename V > typename enable_if< is_class< T >::value != 0 >::type fill_v(T &t, const V &v) { for(auto &e : t) fill_v(e, v); } template< typename F > struct FixPoint : F { explicit FixPoint(F &&f) : F(forward< F >(f)) {} template< typename... Args > decltype(auto) operator()(Args &&... args) const { return F::operator()(*this, forward< Args >(args)...); } }; template< typename F > inline decltype(auto) MFP(F &&f) { return FixPoint{forward(f)}; } #line 1 "structure/union-find/union-find-undo.hpp" struct UnionFindUndo { vector< int > data; stack< pair< int, int > > history; UnionFindUndo(int sz) { data.assign(sz, -1); } bool unite(int x, int y) { x = find(x), y = find(y); history.emplace(x, data[x]); history.emplace(y, data[y]); if(x == y) return (false); if(data[x] > data[y]) swap(x, y); data[x] += data[y]; data[y] = x; return (true); } int find(int k) { if(data[k] < 0) return (k); return (find(data[k])); } int size(int k) { return (-data[find(k)]); } void undo() { data[history.top().first] = history.top().second; history.pop(); data[history.top().first] = history.top().second; history.pop(); } void snapshot() { while(history.size()) history.pop(); } void rollback() { while(history.size()) undo(); } }; int main() { int H, W, N, D; cin >> H >> W >> N >> D; auto mp = make_v< int >(H, W); fill_v(mp, -1); UnionFindUndo uf(N + 1); int sz = N, one = 0; for(int i = 0; i < N; i++) { int y, x; cin >> y >> x; --y, --x; auto unite = [&](int ny, int nx) { if(ny < 0 or nx < 0 or ny >= H or nx >= W) { return; } if(~mp[ny][nx]) { sz -= uf.unite(mp[ny][nx], i); } }; for(int j = 0; j <= D; j++) { for(int k = 0; j + k <= D; k++) { unite(y + j, x + k); unite(y - j, x + k); unite(y + j, x - k); unite(y - j, x - k); } } mp[y][x] = i; } for(int i = 0; i < N; i++) { if(uf.size(i) == 1) ++one; } int small = inf, large = 0; for(int y = 0; y < H; y++) { for(int x = 0; x < W; x++) { if(mp[y][x] == -1) { uf.snapshot(); int sz2 = sz + 1; int one2 = one + 1; set< int > st; auto unite2 = [&](int ny, int nx) { if(ny < 0 or nx < 0 or ny >= H or nx >= W) { return; } if(~mp[ny][nx]) { if(uf.size(mp[ny][nx]) == 1) st.emplace(uf.find(mp[ny][nx])); } }; auto unite = [&](int ny, int nx) { if(ny < 0 or nx < 0 or ny >= H or nx >= W) { return; } if(~mp[ny][nx]) { sz2 -= uf.unite(mp[ny][nx], N); } }; for(int j = 0; j <= D; j++) { for(int k = 0; j + k <= D; k++) { unite2(y + j, x + k); unite2(y - j, x + k); unite2(y + j, x - k); unite2(y - j, x - k); } } for(int j = 0; j <= D; j++) { for(int k = 0; j + k <= D; k++) { unite(y + j, x + k); unite(y - j, x + k); unite(y + j, x - k); unite(y - j, x - k); } } one2 -= uf.size(N) > 1; one2 -= st.size(); chmin(small, sz2 - one2); chmax(large, sz2 - one2); uf.rollback(); } } } cout << small << " " << large << endl; }