#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 2010

enum Type{
	EXIST,
	BAKED,
};

enum Instruction{
	Eat,
	Count,
	Bake_start,
	Bake_complete,
};

struct Info{
	Info(){
		time = 0;
		instruction = Count;
		row_1 = 0;
		col_1 = 0;
		row_2 = 0;
		col_2 = 0;
		priority = 0;
	}

	Info(int arg_time,Instruction arg_instruction,int arg_row_1,
			int arg_col_1,int arg_row_2,int arg_col_2,int arg_priority){
		time = arg_time;
		instruction = arg_instruction;
		row_1 = arg_row_1;
		col_1 = arg_col_1;
		row_2 = arg_row_2;
		col_2 = arg_col_2;
		priority = arg_priority;
	}
	bool operator<(const struct Info &arg) const{
		if(time != arg.time){
			return time > arg.time; //時間の昇順(PQ)
		}else{
			return priority > arg.priority; //優先度の昇順(PQ)
		}
	}
	int time,row_1,col_1,row_2,col_2,priority;
	Instruction instruction;
};

int H,W,T,num_query;
int BIT[NUM][NUM][2];

void add(int add_row,int add_col,int value,Type type){
	for(int row = add_row; row <= H; row += row & -row){
		for(int col = add_col; col <= W; col += col & -col){
			BIT[row][col][type] += value;
		}
	}
}

int getSum(int get_row,int get_col,Type type){
	int ret = 0;
	for(int row = get_row; row > 0; row -= row & -row){
		for(int col = get_col; col > 0; col -= col & -col){
			ret += BIT[row][col][type];
		}
	}
	return ret;
}

int getSum(int from_row,int from_col,int to_row,int to_col,Type type){
	return getSum(to_row,to_col,type)-getSum(from_row-1,to_col,type)-getSum(to_row,from_col-1,type)+getSum(from_row-1,from_col-1,type);
}

int main(){

	scanf("%d %d %d %d",&H,&W,&T,&num_query);

	for(int row = 0; row <= H; row++){
		for(int col = 0; col <= W; col++){
			BIT[row][col][EXIST] = 0;
			BIT[row][col][BAKED] = 0;
		}
	}

	priority_queue<Info> Q;

	int tmp_time,command,row_1,col_1,row_2,col_2;

	//焼き上がりイベントを追加するので、いったん全てPQに突っ込む
	for(int loop = 0; loop < num_query; loop++){
		scanf("%d %d",&tmp_time,&command);
		switch(command){
		case 0:
			scanf("%d %d",&row_1,&col_1);
			Q.push(Info(tmp_time,Bake_start,row_1,col_1,-1,-1,0));
			Q.push(Info(tmp_time+T,Bake_complete,row_1,col_1,-1,-1,0));
			break;
		case 1:
			scanf("%d %d",&row_1,&col_1);
			Q.push(Info(tmp_time,Eat,row_1,col_1,-1,-1,1));
			break;
		case 2:
			scanf("%d %d %d %d",&row_1,&col_1,&row_2,&col_2);
			Q.push(Info(tmp_time,Count,row_1,col_1,row_2,col_2,1));
			break;
		}
	}

	int exist_num,baked_num;
	Info query;

	while(!Q.empty()){
		query = Q.top();
		Q.pop();

		switch(query.instruction){
		case Bake_start:
			add(query.row_1,query.col_1,1,EXIST); //存在テーブルに加算
			break;
		case Bake_complete:
			add(query.row_1,query.col_1,1,BAKED); //焼き上がりテーブルに加算
			break;
		case Eat:
			if(getSum(query.row_1,query.col_1,query.row_1,query.col_1,BAKED) == 1){ //焼きあがっている場合
				add(query.row_1,query.col_1,-1,BAKED); //焼き上がりテーブルから削除
				add(query.row_1,query.col_1,-1,EXIST); //存在テーブルから削除
			}
			break;
		case Count:
			exist_num = getSum(query.row_1,query.col_1,query.row_2,query.col_2,EXIST);
			baked_num = getSum(query.row_1,query.col_1,query.row_2,query.col_2,BAKED);
			printf("%d %d\n",baked_num,exist_num-baked_num);
			break;
		}
	}

	return 0;
}



Measured memory_bytes: 41584