← Home
package main

import (
	"io"
	"os"
	"strconv"
)

type Query struct {
	t int
	x int
	y int
}

type Point struct {
	x      int
	y      int
	gid    int
	dirIdx int
	pos    int
}

type Group struct {
	active []int
}

var (
	dirIndex   map[uint64]int
	pointID    map[uint64]int
	groupIndex map[int64]int
	points     []Point
	groups     []Group
	dirCnt     []int
	pairCnt    []int
	activeN    int
	hasDirs    bool
)

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

func normKey(x, y int) uint64 {
	g := gcd(x, y)
	x /= g
	y /= g
	return uint64(uint32(x))<<32 | uint64(uint32(y))
}

func packPoint(x, y int) uint64 {
	return uint64(uint32(x))<<32 | uint64(uint32(y))
}

func createPoint(x, y int) int {
	len2 := int64(x)*int64(x) + int64(y)*int64(y)
	gid, ok := groupIndex[len2]
	if !ok {
		gid = len(groups)
		groupIndex[len2] = gid
		groups = append(groups, Group{})
	}
	dirIdx := -1
	if hasDirs {
		if idx, ok := dirIndex[normKey(x, y)]; ok {
			dirIdx = idx
		}
	}
	id := len(points)
	points = append(points, Point{x: x, y: y, gid: gid, dirIdx: dirIdx, pos: -1})
	return id
}

func insertPoint(id int) {
	p := points[id]
	g := &groups[p.gid]
	if hasDirs {
		for _, oid := range g.active {
			op := points[oid]
			sx := p.x + op.x
			sy := p.y + op.y
			gg := gcd(sx, sy)
			key := uint64(uint32(sx/gg))<<32 | uint64(uint32(sy/gg))
			if idx, ok := dirIndex[key]; ok {
				pairCnt[idx]++
			}
		}
	}
	if p.dirIdx >= 0 {
		dirCnt[p.dirIdx]++
	}
	points[id].pos = len(g.active)
	g.active = append(g.active, id)
	activeN++
}

func deletePoint(id int) {
	p := points[id]
	g := &groups[p.gid]
	if hasDirs {
		for _, oid := range g.active {
			if oid == id {
				continue
			}
			op := points[oid]
			sx := p.x + op.x
			sy := p.y + op.y
			gg := gcd(sx, sy)
			key := uint64(uint32(sx/gg))<<32 | uint64(uint32(sy/gg))
			if idx, ok := dirIndex[key]; ok {
				pairCnt[idx]--
			}
		}
	}
	if p.dirIdx >= 0 {
		dirCnt[p.dirIdx]--
	}
	pos := p.pos
	last := len(g.active) - 1
	lastID := g.active[last]
	g.active[pos] = lastID
	points[lastID].pos = pos
	g.active = g.active[:last]
	points[id].pos = -1
	activeN--
}

func main() {
	data, _ := io.ReadAll(os.Stdin)
	ptr := 0
	nextInt := func() int {
		for ptr < len(data) && (data[ptr] < '0' || data[ptr] > '9') {
			ptr++
		}
		n := 0
		for ptr < len(data) && data[ptr] >= '0' && data[ptr] <= '9' {
			n = n*10 + int(data[ptr]-'0')
			ptr++
		}
		return n
	}

	q := nextInt()
	queries := make([]Query, q)
	qdirSet := make(map[uint64]struct{})

	for i := 0; i < q; i++ {
		t := nextInt()
		x := nextInt()
		y := nextInt()
		queries[i] = Query{t: t, x: x, y: y}
		if t == 3 {
			qdirSet[normKey(x, y)] = struct{}{}
		}
	}

	dirIndex = make(map[uint64]int, len(qdirSet))
	idx := 0
	for k := range qdirSet {
		dirIndex[k] = idx
		idx++
	}
	hasDirs = len(dirIndex) > 0

	pointID = make(map[uint64]int)
	groupIndex = make(map[int64]int)
	points = make([]Point, 0)
	groups = make([]Group, 0)
	dirCnt = make([]int, len(dirIndex))
	pairCnt = make([]int, len(dirIndex))

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

	for _, qu := range queries {
		switch qu.t {
		case 1:
			pk := packPoint(qu.x, qu.y)
			id, ok := pointID[pk]
			if !ok {
				id = createPoint(qu.x, qu.y)
				pointID[pk] = id
			}
			insertPoint(id)
		case 2:
			id := pointID[packPoint(qu.x, qu.y)]
			deletePoint(id)
		case 3:
			k := normKey(qu.x, qu.y)
			i := dirIndex[k]
			ans := activeN - dirCnt[i] - 2*pairCnt[i]
			out = strconv.AppendInt(out, int64(ans), 10)
			out = append(out, '\n')
		}
	}

	os.Stdout.Write(out)
}