← Home
package main

import (
	"container/heap"
	"io"
	"os"
	"strconv"
)

type FastScanner struct {
	data []byte
	idx  int
	n    int
}

func NewFastScanner() *FastScanner {
	data, _ := io.ReadAll(os.Stdin)
	return &FastScanner{data: data, n: len(data)}
}

func (fs *FastScanner) NextInt64() int64 {
	for fs.idx < fs.n {
		c := fs.data[fs.idx]
		if c >= '0' && c <= '9' {
			break
		}
		fs.idx++
	}
	var v int64
	for fs.idx < fs.n {
		c := fs.data[fs.idx]
		if c < '0' || c > '9' {
			break
		}
		v = v*10 + int64(c-'0')
		fs.idx++
	}
	return v
}

type Op struct {
	typ int
	a   int64
	b   int64
}

type Entry struct {
	v int64
	i int
}

type TreasureHeap []Entry

func (h TreasureHeap) Len() int { return len(h) }
func (h TreasureHeap) Less(i, j int) bool {
	if h[i].v != h[j].v {
		return h[i].v > h[j].v
	}
	return h[i].i < h[j].i
}
func (h TreasureHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *TreasureHeap) Push(x interface{}) {
	*h = append(*h, x.(Entry))
}
func (h *TreasureHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[:n-1]
	return x
}

type Node struct {
	d int64
	r int
}

type NodeHeap []Node

func (h NodeHeap) Len() int { return len(h) }
func (h NodeHeap) Less(i, j int) bool { return h[i].d < h[j].d }
func (h NodeHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *NodeHeap) Push(x interface{}) {
	*h = append(*h, x.(Node))
}
func (h *NodeHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[:n-1]
	return x
}

func gcd(a, b int64) int64 {
	for b != 0 {
		a, b = b, a%b
	}
	return a
}

func buildState(steps []int64, maxOffset int64) (int64, int64, []int64) {
	g := steps[0]
	for i := 1; i < len(steps); i++ {
		g = gcd(g, steps[i])
	}

	norm := make([]int64, len(steps))
	minNorm := int64(1<<62)
	for i, s := range steps {
		ns := s / g
		norm[i] = ns
		if ns < minNorm {
			minNorm = ns
		}
	}

	m := minNorm
	M := int(m)
	limit := maxOffset / g
	inf := limit + 1

	dist := make([]int64, M)
	for i := 0; i < M; i++ {
		dist[i] = inf
	}
	dist[0] = 0

	if M == 1 || limit == 0 {
		return g, m, dist
	}

	mods := make([]int, len(norm))
	ws := make([]int64, len(norm))
	for i, s := range norm {
		mods[i] = int(s % m)
		ws[i] = s
	}

	pq := &NodeHeap{{d: 0, r: 0}}
	heap.Init(pq)

	for pq.Len() > 0 {
		cur := heap.Pop(pq).(Node)
		if cur.d != dist[cur.r] {
			continue
		}
		rem := limit - cur.d
		for i, w := range ws {
			if w > rem {
				continue
			}
			nr := cur.r + mods[i]
			if nr >= M {
				nr -= M
			}
			nd := cur.d + w
			if nd < dist[nr] {
				dist[nr] = nd
				heap.Push(pq, Node{d: nd, r: nr})
			}
		}
	}

	return g, m, dist
}

func computeReachStages(k int64, adds []int64, offsets []int64, h int64) []int {
	res := make([]int, len(offsets))
	for i := range res {
		res[i] = -1
	}

	steps := make([]int64, 1, 1+len(adds))
	steps[0] = k
	maxOffset := h - 1
	remaining := len(offsets)

	for stage := 0; stage <= len(adds); stage++ {
		if stage > 0 {
			steps = append(steps, adds[stage-1])
		}
		g, m, dist := buildState(steps, maxOffset)
		for i, d := range offsets {
			if res[i] != -1 {
				continue
			}
			if d%g != 0 {
				continue
			}
			t := d / g
			if dist[int(t%m)] <= t {
				res[i] = stage
				remaining--
			}
		}
		if remaining == 0 {
			break
		}
	}

	return res
}

func main() {
	fs := NewFastScanner()

	h := fs.NextInt64()
	n := int(fs.NextInt64())
	q := int(fs.NextInt64())
	k := fs.NextInt64()

	offsets := make([]int64, n)
	values := make([]int64, n)
	for i := 0; i < n; i++ {
		a := fs.NextInt64()
		c := fs.NextInt64()
		offsets[i] = a - 1
		values[i] = c
	}

	ops := make([]Op, q)
	adds := make([]int64, 0, 21)

	for i := 0; i < q; i++ {
		t := int(fs.NextInt64())
		if t == 1 {
			x := fs.NextInt64()
			ops[i] = Op{typ: 1, a: x}
			adds = append(adds, x)
		} else if t == 2 {
			x := fs.NextInt64()
			y := fs.NextInt64()
			ops[i] = Op{typ: 2, a: x - 1, b: y}
		} else {
			ops[i] = Op{typ: 3}
		}
	}

	reachStage := computeReachStages(k, adds, offsets, h)

	activateAt := make([][]int, len(adds)+1)
	for i, st := range reachStage {
		if st >= 0 {
			activateAt[st] = append(activateAt[st], i)
		}
	}

	active := make([]bool, n)
	removed := make([]bool, n)

	th := &TreasureHeap{}
	heap.Init(th)

	for _, idx := range activateAt[0] {
		active[idx] = true
		heap.Push(th, Entry{v: values[idx], i: idx})
	}

	out := make([]byte, 0, q*12)
	stage := 0

	for _, op := range ops {
		switch op.typ {
		case 1:
			stage++
			for _, idx := range activateAt[stage] {
				active[idx] = true
				heap.Push(th, Entry{v: values[idx], i: idx})
			}
		case 2:
			idx := int(op.a)
			values[idx] -= op.b
			if active[idx] && !removed[idx] {
				heap.Push(th, Entry{v: values[idx], i: idx})
			}
		case 3:
			for th.Len() > 0 {
				top := (*th)[0]
				idx := top.i
				if removed[idx] || !active[idx] || top.v != values[idx] {
					heap.Pop(th)
					continue
				}
				break
			}
			if th.Len() == 0 {
				out = strconv.AppendInt(out, 0, 10)
				out = append(out, '\n')
			} else {
				top := heap.Pop(th).(Entry)
				idx := top.i
				removed[idx] = true
				active[idx] = false
				out = strconv.AppendInt(out, values[idx], 10)
				out = append(out, '\n')
			}
		}
	}

	os.Stdout.Write(out)
}