```go
package main
import (
"bufio"
"fmt"
"os"
"sort"
)
type Line struct {
A, B, C int64
}
type EntryType int
const (
TypeSegment EntryType = 0
TypeQuery EntryType = 1
)
type Entry struct {
L Line
Type EntryType
Start int64
End int64
}
type Event struct {
Pos int64
Type int // 0: Start, 1: Query, 2: End
}
func abs(x int64) int64 {
if x < 0 {
return -x
}
return x
}
func gcd(a, b int64) int64 {
for b != 0 {
a, b = b, a%b
}
return a
}
func normalize(A, B, C int64) Line {
if A < 0 {
A, B, C = -A, -B, -C
} else if A == 0 {
if B < 0 {
B, C = -B, -C
} else if B == 0 && C < 0 {
C = -C
}
}
g := gcd(abs(A), gcd(abs(B), abs(C)))
return Line{A / g, B / g, C / g}
}
func getLineFromSegment(x1, y1, x2, y2 int64) Line {
A := y1 - y2
B := x2 - x1
C := -A*x1 - B*y1
return normalize(A, B, C)
}
func getBisectorLine(x1, y1, x2, y2 int64) Line {
A := 2 * (x1 - x2)
B := 2 * (y1 - y2)
C := -(x1*x1 - x2*x2 + y1*y1 - y2*y2)
return normalize(A, B, C)
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords)
readInt := func() int64 {
scanner.Scan()
text := scanner.Bytes()
var n int64
sign := int64(1)
start := 0
if len(text) > 0 && text[0] == '-' {
sign = -1
start = 1
}
for i := start; i < len(text); i++ {
n = n*10 + int64(text[i]-'0')
}
return n * sign
}
// Read N and M. Check scanner error implicitly by assuming valid input.
// We need to call readInt() once to prime the scanner if we used Scan() manually,
// but here we just call readInt() directly.
if !scanner.Scan() {
return
}
// Parse N manually from the first token
text := scanner.Bytes()
var nVal int64
for i := 0; i < len(text); i++ {
nVal = nVal*10 + int64(text[i]-'0')
}
N := int(nVal)
M := int(readInt())
entries := make([]Entry, 0, N+M*M/2)
for i := 0; i < N; i++ {
x1, y1, x2, y2 := readInt(), readInt(), readInt(), readInt()
L := getLineFromSegment(x1, y1, x2, y2)
var l, r int64
if L.B == 0 {
l, r = 2*y1, 2*y2
} else {
l, r = 2*x1, 2*x2
}
if l > r {
l, r = r, l
}
entries = append(entries, Entry{L, TypeSegment, l, r})
}
type Circle struct {
x, y, r int64
}
circles := make([]Circle, M)
for i := 0; i < M; i++ {
circles[i] = Circle{readInt(), readInt(), readInt()}
}
for i := 0; i < M; i++ {
for j := i + 1; j < M; j++ {
if circles[i].r != circles[j].r {
continue
}
dx := circles[i].x - circles[j].x
dy := circles[i].y - circles[j].y
distSq := dx*dx + dy*dy
// Condition: circles don't share common points => dist > 2*r => dist^2 > 4*r^2
if distSq <= 4*circles[i].r*circles[i].r {
continue
}
L := getBisectorLine(circles[i].x, circles[i].y, circles[j].x, circles[j].y)
var pos int64
if L.B == 0 {
pos = circles[i].y + circles[j].y
} else {
pos = circles[i].x + circles[j].x
}
entries = append(entries, Entry{L, TypeQuery, pos, 0})
}
}
sort.Slice(entries, func(i, j int) bool {
li, lj := entries[i].L, entries[j].L
if li.A != lj.A {
return li.A < lj.A
}
if li.B != lj.B {
return li.B < lj.B
}
return li.C < lj.C
})
var ans int64
nEntries := len(entries)
i := 0
events := make([]Event, 0) // Reuse buffer
for i < nEntries {
j := i
// Find range with same Line
for j < nEntries && entries[j].L == entries[i].L {
j++
}
// Process range [i, j)
events = events[:0]
for k := i; k < j; k++ {
if entries[k].Type == TypeSegment {
events = append(events, Event{entries[k].Start, 0})
events = append(events, Event{entries[k].End, 2})
} else {
events = append(events, Event{entries[k].Start, 1})
}
}
sort.Slice(events, func(a, b int) bool {
if events[a].Pos != events[b].Pos {
return events[a].Pos < events[b].Pos
}
// Priority: Start(0) < Query(1) < End(2)
return events[a].Type < events[b].Type
})
count := int64(0)
for _, e := range events {
if e.Type == 0 { // Start
count++
} else if e.Type == 1 { // Query
ans += count
} else { // End
count--
}
}
i = j
}
fmt.Println(ans)
}
```