#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) {
	//[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 (N == 1 || N == scale - 1 || 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 (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 {
		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;
	}

	//2分割したどちらかに爆弾が集中していれば、そちらだけ解いて終了。
	const int lowN = mycout(low[0],low[1],low[2],low[3]);
	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);
	}

	//上で二分割した区間lowとhighを、更にcenterとedgeに均等に二分割する。
	//このとき、low_centerとhigh_centerをまとめた区間が長方形になるように分割する。
	assert(max(dy, dx) <= min(dy, dx) * 2);
	int lowc[4], highc[4], lowe[4], highe[4], center[4];
	if (dy < dx) {
		const int medx = (xlb + xub) / 2;
		const int medy = (ylb + yub) / 2;
		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;
	}
	else {
		const int medx = (xlb + xub) / 2;
		const int medy = (ylb + yub) / 2;
		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;
	}
	const int centerN = mycout(center[0], center[1], center[2], center[3]);
	if (centerN == 0) {
		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) {
		ans += solve(lowc[0], lowc[1], lowc[2], lowc[3], lowN);
		ans += solve(highc[0], highc[1], highc[2], highc[3], N - lowN);
		return ans;
	}
	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: 724