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)
}