#include using namespace std; using ll = long long; using ull = unsigned long long; //using uLL = unsigned __int128; template using pqg = priority_queue, greater>; template using pq = priority_queue; using pll = pair; using pii = pair; using vvvvl = vector>>>; using vvvi = vector>>; using vvvl = vector>>; using vvvc = vector>>; using vvvd = vector>>; using vvi = vector>; using vvl = vector>; using vvc = vector>; using vvp = vector>>; using vvb = vector>; using vvd = vector>; using vp = vector>; using vi = vector; using vl = vector; using vu = vector; using vc = vector; using vb = vector; using vd = vector; #define vvvm vector>> #define vvm vector> #define vm vector template using umap = unordered_map; template using uset = unordered_set; #define rrr(l, r) mt()%(r-l+1)+l #define rep(i, s, f) for(long long i = s; i <= f; i++) #define rep1(i, s, f) for(long long i = s; i <= f; i++) #define per(i, s, f) for(long long i = s; i >= f; i--) #define per1(i, s, f) for(long long i = s; i >= f; i--) #define all0(x) (x).begin() ,(x).end() #define all(x) (x).begin() + 1, (x).end() #define ins(l, r, x) (l <= x && x <= r) #define ENDL '\n' //////////////////////////////////////////////////////////////////////////////////////////////////////////// //これが本当の組み込み関数ってね(笑) template int LB(vector &A, T x) { return lower_bound(A.begin(), A.end(), x) - A.begin(); } template int UB(vector &A, T x) { return upper_bound(A.begin(), A.end(), x) - A.begin(); } template void UNIQUE(vector &A, int indexed) { sort(A.begin() + indexed, A.end()); A.erase(unique(A.begin() + indexed, A.end()), A.end()); } int msb(long long a) { if(a == 0) return -1; return 64 - int(__builtin_clzll(a)); } template void chmin(T &a, T b) { a = min(a, b); } template void chmax(T &a, T b) { a = max(a, b); } ////////////////////////////////////////////////////////////////////// //数学系 /////////////////////////////////////////////////////////////////////// ll extgcd (ll a, ll b, ll &x, ll &y) { if(b == 0) { x = 1;y = 0;return a;} ll d = extgcd(b, a%b, y, x); y -= a/b * x; return d; } template T ceil__(T a, T b) { assert(b != 0); if(a % b == 0) return a / b; if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b + 1; else return a / b; } template T floor__(T a, T b) { assert(b != 0); if(a % b == 0) return a / b; if((a <= 0 && b < 0) || (a >= 0 && b > 0)) return a/b; else return a/b - 1; } ll modpow(ll x, ll y, ll mod) { if(x > mod) x %= mod; if(y == 0) return 1; ll res = modpow(x, y >> 1, mod); res = res * res % mod; if(y & 1) res *= x, res %= mod; return res; } ll sqrt__(ll a) { ll l = 0; ll r = 3037000499LL; while(l < r) { ll mid = (l + r + 1) / 2; if(mid * mid <= a) l = mid; else r = mid - 1; } return l; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //グローバル変数を置くところ //#pragma GCC optimize("O3") //#pragma GCC optimize("unroll-loops") //#pragma GCC optimize "trapv" //お祈り。変数同士の演算でのみ感知。代入や、 a *= 10000 では感知しない。 const ll big = 1001001001; const ll BIG = 1001001001001001001LL; const double pi = 3.141592653589793; //const ll mod = 1000000007; //const ll mod = 998244353; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct Union { vector Par; vector size; int S; Union(int N) { Par.resize(N+1); size.resize(N+1, 1); S = 0; for (int i = 1; i <= N; i++) { Par[i] = i; } } int root(int u) { if (Par[u] != u) { return Par[u] = root(Par[u]); } return u; } bool same(int a, int b) { return root(a) == root(b); } void unit(int a, int b) { a = root(a), b = root(b); if(size[a] < size[b]) swap(a, b); if(a == b) return; else { if(size[a] == 1 && size[b] == 1) S++; else if(size[a] > 1 && size[b] > 1) S--; Par[b] = root(a); size[a] += size[b]; } } int getsize(int u) {//uが含まれる連結成分のサイズを返す return size[root(u)]; }//O(1)(大体) int sum() { return S; } }; void solve() { int H, W, N, D; cin >> H >> W >> N >> D; vi X(N+1), Y(N+1); vvi table(H+1, vi(W+1, 0)); rep(i,1,N) cin >> Y[i] >> X[i]; rep(i,1,N) table[Y[i]][X[i]] = i; Union uni(N); rep(i,1,N) { rep(dx, -D, D) { rep(dy, -(D - abs(dx)), D - abs(dx)) { int nx = X[i] + dx; int ny = Y[i] + dy; if(ins(1, H, ny) && ins(1, W, nx)) { if(table[ny][nx]) uni.unit(i, table[ny][nx]); } } } } int m = N; int M = 0; rep(x,1,W) { rep(y, 1, H) { vector> cnt(3); rep(dx, -D, D) { rep(dy, -(D - abs(dx)), D - abs(dx)) { int nx = x + dx; int ny = y + dy; if(ins(1, H, ny) && ins(1, W, nx)) { if(table[ny][nx]) { cnt[min(2,uni.getsize(table[ny][nx]))].insert(uni.root(table[ny][nx])); } } } } int res = uni.sum(); if(cnt[1].size() >= 1) res++; if(cnt[2].size() >= 1) res -= cnt[2].size()-1; chmax(M, res); chmin(m, res); } } cout << m << " " << M << endl; } /* -違法な読み替えをしていないか・違法な操作をしていないか ・実装が重そうな時は確認する 一度飛ばしても良い ・waが出た時は嘘を吐いていないかの他に、違法な操作をしていないかもチェックする 特にふわっとした操作は危ない ・区間をちょっと伸ばす←伸ばす余地はあるか ・選択肢のうち、どれでも良いから消す←本当にどれでも良いか 場合分けを挟んでないか -その情報はdpで纏めて良いものか ・dpで持つ:「この先の探索をするのに必要な情報」 なぜ必要なのか? ・途中までは辻褄が合っても、最後に分岐とかはある -何もわからないという時 観察が求められるのは主に、[1]より高速な計算が求められる時 [2]最適解が求められる時 である。逆に、これを求められたら観察が必要になる。 ・解を列挙した上で特徴づけ -数え上げる対象・或いは最適化問題の解・クエリ問題で計算する対象」に、制約によって良い性質が与えられている可能性を探る -機械で列挙し、最適解の構造を得る -数え上げる対象が少ないと直感できるが、直接計算が難しそうな時 -可能か?を探る際は、最悪ケースで実験する。穴がある解を未然に防ぐ。 ・遷移を図で表現し、使いまわしたりできないか見る ・自由度が高い最適化では、上階が達成できがち 必要条件をつみかさねていく ”非自明な最悪ケース(例:点の数が多いのに答え=0)”を作る時、何を意識したか? ・解の形を、必要条件や貪欲から絞る ・無駄な事/非効率な事は禁止して、その上で何ができるか ・差分を計算するなら、最初から差分計算の処理のみで答えを計算した方が良い。最初を愚直でやると情報をロストする可能性があるし、愚直の枠組みに囚われる事もある。 ・要素と制約をグラフで表してみる ・強い制約は、数え上げ・最適化・クエリ・ゲーム共に良い性質を生みやすいので注目する -制約に具体的な数値が登場している←適当に解をいじると何となく性質が見えることが多い ・制約を「全ての要素についてoo ⇔ (1<=i<=N)で、iについてoo」といったように、独立なものに言い換えた方がやりやすいこともある ・よくわからないものを言い換える。 -慣れ親しんだ制約も、問題設定によってより扱いやすい形に言い換えられるかもしれない(例:グラフが連結) -一つの解法で駄目な時、周辺の解法も試すようにする。周辺で見えないなら全部試す。 ・「貪欲で最適解」と「解の形を絞って全探索」 ・「dp/最適化」と「解の形を絞って全探索」と 上界を見積もる」 ・「dp」と「必要な情報を言い換える・何でのその情報が必要なのか考える事による高速化」 ・「dp」と「探索の順番を変更する: でかい方から、深い方から、DAGで先端になっている・或いは葉になっている所から、全てを逆に言い換えて時系列的に後ろから」 ・「dp」と「軸にするものを変える: マスを軸にするのではなく、燃料を軸にするなど」 */ int main() { ios::sync_with_stdio(false); std::cin.tie(nullptr); cout << fixed << setprecision(15); ll T = 1; //cin >> T; rep(i, 1, T) { solve(); } return 0; }