#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<vector>
#include<array>
#include<string>
#include<stack>
#include<queue>
#include<algorithm>
#include<cassert>
#include<functional>
#include<random>
#include<complex>
#include<bitset>
#include<chrono>
//#include<boost/multiprecision/cpp_int.hpp>
#define int int64_t
#define uint uint64_t
#define REP(i, a, b) for (int64_t i = (int64_t)(a); i < (int64_t)(b); i++)
#define rep(i, a) REP(i, 0, a)
#define EACH(i, a) for (auto i: a)
#define ITR(x, a) for (auto x = a.begin(); x != a.end(); x++)
#define ALL(a) (a.begin()), (a.end())
#define HAS(a, x) (a.find(x) != a.end())
#define Min(x) *min_element(ALL(x))
#define Max(x) *max_element(ALL(x))
#define Unique(L) (L.erase(unique(ALL(L)), L.end()))
#define veccat(v1, v2) std::copy((v2).begin(),(v2).end(),std::back_inserter(v1)/*v1の後ろにv2を入れる*/)
#define intmax (std::numeric_limits<int64_t>::max() / 4)
using namespace std;
//typedef boost::multiprecision::cpp_int bigint;
const double EPS = 1e-9;
const double PI = acos(-1.0);

int exist[50][50];

int mycout(int ylb, int yub, int xlb, int xub) {
	//[ylb,yub)行目の[xlb,xub)に何個の爆弾があるか聞く。
	cout << "? " << ylb << " " << xlb << " " << yub - 1 << " " << xub - 1 << endl;
	int ans;
	cin >> ans;
	return ans;
}

int solve(int ylb, int yub, int xlb, int xub, int N, int cLen = -1, int cN = -1, bool cx = true) {
	//[ylb,yub)行目の[xlb,xub)にN個の爆弾があるとして、解く

	if (N == 0)return 0;

	const int dy = yub - ylb;
	const int dx = xub - xlb;
	const int scale = dy * dx;
	if (scale == N) {
		REP(y,ylb,yub)REP(x, xlb, xub)exist[y][x] = 1;
		return N;
	}
	int ans = 0;

	//問題が小さくなったら普通に二分探索で解く。
	if (cLen == -1) {
		if (scale <= 3) {
			if (dy < dx) {
				const int medx = (xlb + xub) / 2;
				const int hN = mycout(ylb, yub, xlb, medx);
				ans += solve(ylb, yub, xlb, medx, hN);
				ans += solve(ylb, yub, medx, xub, N - hN);
				return ans;
			}
			else {
				const int medy = (ylb + yub) / 2;
				const int hN = mycout(ylb, medy, xlb, xub);
				ans += solve(ylb, medy, xlb, xub, hN);
				ans += solve(medy, yub, xlb, xub, N - hN);
				return ans;
			}
		}
	}

	//区間を均等に2分割する。
	int low[4], high[4];
	if (cLen == -1) {
		if (dy < dx) {
			const int med = (xlb + xub) / 2;
			low[0] = ylb; low[1] = yub; low[2] = xlb; low[3] = med;
			high[0] = ylb; high[1] = yub; high[2] = med; high[3] = xub;
		}
		else {
			cx = false;
			const int med = (ylb + yub) / 2;
			low[0] = ylb; low[1] = med; low[2] = xlb; low[3] = xub;
			high[0] = med; high[1] = yub; high[2] = xlb; high[3] = xub;
		}
	}
	else {
		if (cx == true) {
			const int med = xlb + cLen;
			low[0] = ylb; low[1] = yub; low[2] = xlb; low[3] = med;
			high[0] = ylb; high[1] = yub; high[2] = med; high[3] = xub;
		}
		else {
			const int med = ylb + cLen;
			low[0] = ylb; low[1] = med; low[2] = xlb; low[3] = xub;
			high[0] = med; high[1] = yub; high[2] = xlb; high[3] = xub;
		}
	}
	const int low_scale = (low[1] - low[0]) * (low[3] - low[2]);
	const int high_scale = (high[1] - high[0]) * (high[3] - high[2]);

	//2分割したどちらかに爆弾が集中していれば、そちらだけ解いて終了。
	int lowN, highN;
	if (cLen == -1) {
		lowN = mycout(low[0], low[1], low[2], low[3]);
		highN = N - lowN;
		if (lowN == 0) {
			return solve(high[0], high[1], high[2], high[3], N);
		}
		if (lowN == N) {
			return solve(low[0], low[1], low[2], low[3], N);
		}
		if (lowN == low_scale || highN == high_scale) {
			ans += solve(low[0], low[1], low[2], low[3], lowN);
			ans += solve(high[0], high[1], high[2], high[3], highN);
			return ans;
		}
	}
	else {
		lowN = cN;
		highN = N - lowN;
		if (lowN == 0) {
			return solve(high[0], high[1], high[2], high[3], N);
		}
		if (lowN == N) {
			return solve(low[0], low[1], low[2], low[3], N);
		}
		if (lowN == low_scale || highN == high_scale) {
			ans += solve(low[0], low[1], low[2], low[3], lowN);
			ans += solve(high[0], high[1], high[2], high[3], highN);
			return ans;
		}
	}

	//問題が小さくなったら普通に二分探索で解く。
	if (cLen != -1) {
		if (low_scale <= 3 || high_scale <= 3) {
			ans += solve(low[0], low[1], low[2], low[3], lowN);
			ans += solve(high[0], high[1], high[2], high[3], highN);
			return ans;
		}
	}

	//上で二分割した区間lowとhighを、更にcenterとedgeに均等に二分割する。
	//このとき、low_centerとhigh_centerをまとめた区間が長方形になるように分割する。
	int lowc[4], highc[4], lowe[4], highe[4], center[4], edge[4], nextcLen = -1;
	bool nextcx = true, edge_is_rect = false;
	if (cx == true && dx <= dy * 2) {//lowとhighはxで分け、centerとedgeはyで分ける
		const int medx = (xlb + xub) / 2;
		const int medy = (ylb + yub) / 2;
		nextcx = true;
		nextcLen = medx - xlb;
		edge_is_rect = true;
		lowc[0] = ylb; lowc[1] = medy; lowc[2] = xlb; lowc[3] = medx;
		highc[0] = ylb; highc[1] = medy; highc[2] = medx; highc[3] = xub;
		lowe[0] = medy; lowe[1] = yub; lowe[2] = xlb; lowe[3] = medx;
		highe[0] = medy; highe[1] = yub; highe[2] = medx; highe[3] = xub;
		center[0] = ylb; center[1] = medy; center[2] = xlb; center[3] = xub;
		edge[0] = medy; edge[1] = yub; edge[2] = xlb; edge[3] = xub;
	}
	else if (cx == false && dy <= dx * 2) {//lowとhighはyで分け、centerとedgeはxで分ける
		const int medx = (xlb + xub) / 2;
		const int medy = (ylb + yub) / 2;
		nextcx = false;
		nextcLen = medy - ylb;
		edge_is_rect = true;
		lowc[0] = ylb; lowc[1] = medy; lowc[2] = xlb; lowc[3] = medx;
		highc[0] = medy; highc[1] = yub; highc[2] = xlb; highc[3] = medx;
		lowe[0] = ylb; lowe[1] = medy; lowe[2] = medx; lowe[3] = xub;
		highe[0] = medy; highe[1] = yub; highe[2] = medx; highe[3] = xub;
		center[0] = ylb; center[1] = yub; center[2] = xlb; center[3] = medx;
		edge[0] = ylb; edge[1] = yub; edge[2] = medx; edge[3] = xub;
	}
	else if (cx == true && dy * 2 < dx) {//両方xで分ける
		const int medx = (xlb + xub) / 2;
		const int medxl = (xlb + medx) / 2;
		const int medxu = (medx + xub) / 2;
		nextcx = true;
		nextcLen = medx - medxl;
		lowc[0] = ylb; lowc[1] = yub; lowc[2] = medxl; lowc[3] = medx;
		highc[0] = ylb; highc[1] = yub; highc[2] = medx; highc[3] = medxu;
		lowe[0] = ylb; lowe[1] = yub; lowe[2] = xlb; lowe[3] = medxl;
		highe[0] = ylb; highe[1] = yub; highe[2] = medxu; highe[3] = xub;
		center[0] = ylb; center[1] = yub; center[2] = medxl; center[3] = medxu;
	}
	else  if (cx == false && dx * 2 < dy) {//両方yで分ける
		const int medy = (ylb + yub) / 2;
		const int medyl = (ylb + medy) / 2;
		const int medyu = (medy + yub) / 2;
		nextcx = false;
		nextcLen = medy - medyl;
		lowc[0] = medyl; lowc[1] = medy; lowc[2] = xlb; lowc[3] = xub;
		highc[0] = medy; highc[1] = medyu; highc[2] = xlb; highc[3] = xub;
		lowe[0] = ylb; lowe[1] = medyl; lowe[2] = xlb; lowe[3] = xub;
		highe[0] = medyu; highe[1] = yub; highe[2] = xlb; highe[3] = xub;
		center[0] = medyl; center[1] = medyu; center[2] = xlb; center[3] = xub;
	}
	else assert(0);

	//4つに区分けされた各領域の爆弾の数を求めていく。
	const int centerN = mycout(center[0], center[1], center[2], center[3]);
	if (centerN == 0) {
		if (edge_is_rect) {
			return solve(edge[0], edge[1], edge[2], edge[3], N, nextcLen, lowN, nextcx);
		}
		ans += solve(lowe[0], lowe[1], lowe[2], lowe[3], lowN);
		ans += solve(highe[0], highe[1], highe[2], highe[3], N - lowN);
		return ans;
	}
	if (centerN == N) {
		return solve(center[0], center[1], center[2], center[3], N, nextcLen, lowN, nextcx);
	}
	else {
		const int loweN = mycout(lowe[0], lowe[1], lowe[2], lowe[3]);
		ans += solve(lowe[0], lowe[1], lowe[2], lowe[3], loweN);
		ans += solve(lowc[0], lowc[1], lowc[2], lowc[3], lowN - loweN);
		ans += solve(highc[0], highc[1], highc[2], highc[3], centerN + loweN - lowN);
		ans += solve(highe[0], highe[1], highe[2], highe[3], N - loweN - centerN);
		return ans;
	}
}

signed main() {
	cin.tie(0);
	ios::sync_with_stdio(false);

	int H, W, N, K;
	cin >> H >> W >> N >> K;

	int table[50][50];
	int zzz = 1;
	rep(i, H)rep(j, W) {
		table[i][j] = zzz;
		zzz *= 2;
		zzz %= K;
	}

	rep(i, 50)rep(j, 50)exist[i][j] = 0;

	solve(0, H, 0, W, N);

	int ans = 0;
	rep(i, 50)rep(j, 50)if (exist[i][j]) {
		ans += table[i][j];
		ans %= K;
	}
	cout << "! " << ans << endl;

	return 0;
}

Measured memory_bytes: 596