Problem:
E： たい焼きマスターと食べ盛り - Taiyaki-Master and Eater

物語

つたさんはたい焼きを作るのがとても上手い。えびちゃんはそんなつたさんが作るたい焼きが大好きだ。　2 人は毎週たい焼きを作って食べて幸せに過ごしているが、たい焼きを作っているそばで食べ盛りのえびちゃんにつまみ食いをされてしまうのがつたさんの最近の悩みだ。

学校祭でたい焼きを作ることになったつたさんは、いつものようにえびちゃんにつまみ食いされていたら売上が心配なので、自分が注目している範囲のたい焼きの様子を知ることができればいいなと思った。学校祭が成功するように、つたさんを助けてあげよう。

問題

縦 H、横 W の長方形状のたい焼きプレートがあり、たい焼きをセットしてから焼き上がるまでに T 分かかる。このプレートはたい焼きを一度に最大 HW 個焼くことができ、プレートの上から i (1 \leq i \leq H) 番目で左から j (1 \leq j \leq W) 番目の場所で焼いているたい焼きは (i, j) で表される。

次の 3 種類のイベントが合計 Q 回発生する。なお、初期状態ではたい焼きはいずれの場所にもセットされていない。

時刻 t につたさんが (h, w) にたい焼きを 1 つセットする。そのたい焼きは T 分で焼き上がり、時刻 t+T の時点で食べることができるようになる。ただし、すでにたい焼きがセットされている場所にたい焼きをセットすることはない。

時刻 t に、えびちゃんが (h, w) にあるたい焼きをつまみ食いしようとする。ただし、つまみ食いできるのはすでに焼き上がったたい焼きのみである。つまみ食いした後はその場所からたい焼きが無くなる (すなわち、たい焼きがセットされていない状態になる)。たい焼きが焼き上がっていない、またはたい焼きがない場所にもつまみ食いのイベントが発生する場合があることに注意せよ。

左上が (h_1, w_1)、右下が (h_2, w_2) となるような長方形領域内 ((h_1, w_1) および (h_2, w_2) を含む) のたい焼きについて、次の数が時刻 t の時点でそれぞれいくつであるかをつたさんがカウントする。

すでに焼き上がったたい焼きの数

まだ焼き上がっていないたい焼きの数

入力形式

H W T Q
t_1 c_1 h_{11} w_{11} (h_{12} w_{12})
t_2 c_2 h_{21} w_{21} (h_{22} w_{22})
…
t_Q c_Q h_{Q1} w_{Q1} (h_{Q2} w_{Q2})

入力は全て整数で与えられる。

1 行目には、たい焼きプレートの縦 H、 横 W、 たい焼きをセットしてから焼きあがるまでの時間 T、 イベントの数 Q が与えられる。

1+i (1 \leq i \leq Q) 行目には、時刻 t_i で発生したイベントの内容が与えられる。

c_i はイベントの種類を表す。この値が 0 ならばたい焼きをセットするイベント、 1 ならばつまみ食いをするイベント、 2 ならばたい焼きをカウントするイベントを指す。

h_{i1} (1 \leq h_{i1} \leq H)、w_{i1} (1 \leq w_{i1} \leq W) は、c_i = 0, 1 において次のような意味である。

c_i = 0 のとき、(h_{i1}, w_{i1}) の場所にたい焼きを1つセットする。

c_i = 1 のとき、(h_{i1}, w_{i1}) の場所に焼き上がった状態のたい焼きがあればつまみ食いをし、そうでなければ何もしない。

h_{i2}、w_{i2} は、c_i = 2 のときのみ与えられる。

c_i = 2 のとき、左上を (h_{i1}, w_{i1})、 右下を (h_{i2}, w_{i2}) とする長方形領域内のたい焼きについてカウントする。

入出力が非常に多くなることが予想されるため、入出力に遅い関数を使っている場合は注意してください。

制約

1 \leq H \leq 2 \times 10^3

1 \leq W \leq 2 \times 10^3

1 \leq T \leq 10^9

1 \leq Q \leq 10^5

1 \leq t_i \leq 10^9

i \lt j ならば、 t_i \lt t_j

0 \leq c_i \leq 2

1 \leq h_{i1} \leq h_{i2} \leq H

1 \leq w_{i1} \leq w_{i2} \leq W

出力形式

(c_i = 2 (1 \leq i \leq Q) の個数) を n とする。たい焼きのカウントの結果を、次の書き方に従ってイベントの発生時間が早い順に n 行に出力せよ。

a_1 b_1
a_2 b_2
…
a_n b_n

a_i は、領域内にある「焼き上がったたい焼きの数」である。

b_i は、領域内にある「まだ焼き上がっていないたい焼きの数」である。

末尾の改行を忘れないこと。

入力例1

3 3 3 6
1 0 1 1
2 2 1 1 2 2
3 1 1 1
4 0 2 1
5 2 1 1 2 2
6 2 2 1 3 3

出力例1

0 1
1 1
0 1

この入出力例では以下のイベントが発生している。

時刻 1 分に、(1, 1) にたい焼きをセットする。このたい焼きは時刻 4 分に焼き上がる。

時刻 2 分に、左上 (1, 1)、右下 (2, 2) の長方形領域にあるたい焼きをカウントする。(1, 1) にセットしたたい焼きはまだ焼き上がっていないため、0 1 を出力する。

時刻 3 分に、(1, 1) にあるたい焼きをつまみ食いしようとするが、まだ焼き上がっていないので何もしない。

時刻 4 分に、(2, 1) にたい焼きをセットする。このたい焼きは時刻 7 分に焼き上がる。

時刻 5 分に、左上 (1, 1)、右下 (2, 2) の長方形領域にあるたい焼きをカウントする。(1, 1) のたい焼きは焼き上がっており、 (2, 1) のたい焼きはまだ焼き上がっていないため、1 1 を出力する。

時刻 6 分に、左上 (2, 1)、右下 (3, 3) の長方形領域にあるたい焼きをカウントする。(2, 1) のたい焼きはまだ焼き上がっていないため、0 1 を出力する。

Solution in C++11 (judge verdict: Memory Limit Exceeded)

#include<bits/stdc++.h>
using namespace std;
//BEGIN CUT HERE
template <typename T,typename E>
struct SegmentTree{
  typedef function<T(T,T)> F;
  typedef function<T(T,E)> G;
  int n;
  F f;
  G g;
  T d1;
  E d0;
  vector<T> dat;
  SegmentTree(){};
  SegmentTree(int n_,F f,G g,T d1,
	      vector<T> v=vector<T>()){
    this->f=f;
    this->g=g;
    this->d1=d1;
    init(n_);
    if(n_==(int)v.size()) build(n_,v);
  }
  void init(int n_){
    n=1;
    while(n<n_) n*=2;
    dat.clear();
    dat.resize(2*n-1,d1);
  }
  void build(int n_, vector<T> v){
    for(int i=0;i<n_;i++) dat[i+n-1]=v[i];
    for(int i=n-2;i>=0;i--)
      dat[i]=f(dat[i*2+1],dat[i*2+2]);
  }
  void update(int k,E a){
    k+=n-1;
    dat[k]=g(dat[k],a);
    while(k>0){
      k=(k-1)/2;
      dat[k]=f(dat[k*2+1],dat[k*2+2]);
    }
  }
  T query(int a,int b,int k,int l,int r){
    if(r<=a||b<=l) return d1;
    if(a<=l&&r<=b) return dat[k];
    T vl=query(a,b,k*2+1,l,(l+r)/2);
    T vr=query(a,b,k*2+2,(l+r)/2,r);
    return f(vl,vr);
  }
  T query(int a,int b){
    return query(a,b,0,0,n);
  }
};

template <typename T,typename E,typename R>
struct SegmentTree2D{
  typedef function<T(void)> C;
  typedef function<R(R,R)> F;
  typedef function<void(T&,int,E)> G;
  typedef function<R(T&,int,int)> V;
  int n;
  F f;
  G g;
  V v;
  R d1;
  vector<T> dat;
  SegmentTree2D(int n_,C c,F f,G g,V v,R d1){
    this->f=f;
    this->g=g;
    this->v=v;
    this->d1=d1;
    init(n_,c);
  }
  void init(int n_,C c){
    n=1;
    while(n<n_) n*=2;
    dat.clear();
    dat.resize(2*n-1);
    for(int i=0;i<2*n-1;i++) dat[i]=c();
  }
  void update(int x,int y,E e){
    x+=n-1;
    g(dat[x],y,e);
    while(x>0){
      x=(x-1)/2;
      g(dat[x],y,e);
    }
  }
  R query(int a,int b,int c,int d,int k,int l,int r){
    if(r<=a||b<=l) return d1;
    if(a<=l&&r<=b) return v(dat[k],c,d);
    R vl=query(a,b,c,d,k*2+1,l,(l+r)/2);
    R vr=query(a,b,c,d,k*2+2,(l+r)/2,r);
    return f(vl,vr);
  }
  // [a,b) * [c,d)
  R query(int a,int b,int c,int d){
    return query(a,b,c,d,0,0,n);
  }
};

//END CUT HERE
signed main(){
  cin.tie(0);
  ios::sync_with_stdio(0);
  int h,w,t,q;
  cin>>h>>w>>t>>q;
  typedef SegmentTree<int,int> T;
  SegmentTree2D<T,int,int> beet(h+100,
				[&](){return T(w+100,
					       [](int a,int b){return a+b;},
					       [](int a,int b){return a+b;},
					       0);},
				
				[](int a,int b){return a+b;},
				[](T& t,int k,int e){t.update(k,e);},
				[](T& t,int c,int d){return t.query(c,d);},
				0);
  SegmentTree2D<T,int,int> ushi(h+100,
				[&](){return T(w+100,
					       [](int a,int b){return a+b;},
					       [](int a,int b){return a+b;},
					       0);},
				[](int a,int b){return a+b;},
				[](T& t,int k,int e){t.update(k,e);},
				[](T& t,int c,int d){return t.query(c,d);},
				0);
  
  typedef pair<int,int> P;
  typedef pair<int,P> PP;
  queue<PP> qq; 
  for(int i=0;i<q;i++){
    int a,c,x1,y1;
    cin>>a>>c>>x1>>y1;
    while(!qq.empty()&&qq.front().first<=a){
      P p=qq.front().second;qq.pop();
      int x=p.first,y=p.second;
      assert(beet.query(x,x+1,y,y+1)==1);
      beet.update(x,y,-1);
      assert(ushi.query(x,x+1,y,y+1)==0);
      ushi.update(x,y,1);
    }
    if(c==0){
      assert(beet.query(x1,x1+1,y1,y1+1)==0);
      beet.update(x1,y1,1);
      qq.push(PP(a+t,P(x1,y1)));
    }
    if(c==1){
      if(ushi.query(x1,x1+1,y1,y1+1)==0) continue;
      ushi.update(x1,y1,-1);
    }
    if(c==2){
      int x2,y2;
      cin>>x2>>y2;
      x2++;y2++;
      cout<<ushi.query(x1,x2,y1,y2)<<" "<<beet.query(x1,x2,y1,y2)<<endl;
    }
  }
  return 0;
}