```go
package main
import (
"bufio"
"container/heap"
"fmt"
"math"
"math/rand"
"os"
"sort"
"strconv"
)
type Point struct {
id int
x, y int64
}
type Segment struct {
id int
P1, P2 Point
dx, dy int64
}
type NodeData struct {
id int
x, y float64
}
type Node struct {
seg Segment
priority int
left *Node
right *Node
}
func evalAt(S Segment, X int64) (num, den int64) {
num = S.P1.y*S.dx + (X-S.P1.x)*S.dy
den = S.dx
return
}
func isAbove(A, B Segment, X int64) bool {
numA, denA := evalAt(A, X)
numB, denB := evalAt(B, X)
diff := numA*denB - numB*denA
if diff != 0 {
return diff > 0
}
slopeDiff := A.dy*B.dx - B.dy*A.dx
return slopeDiff > 0
}
func split(node *Node, seg Segment, X int64) (*Node, *Node) {
if node == nil {
return nil, nil
}
if isAbove(seg, node.seg, X) {
l, r := split(node.right, seg, X)
node.right = l
return node, r
}
l, r := split(node.left, seg, X)
node.left = r
return l, node
}
func merge(l, r *Node) *Node {
if l == nil {
return r
}
if r == nil {
return l
}
if l.priority > r.priority {
l.right = merge(l.right, r)
return l
}
r.left = merge(l, r.left)
return r
}
func insertNode(node *Node, newNode *Node, X int64) *Node {
if node == nil {
return newNode
}
if newNode.priority > node.priority {
l, r := split(node, newNode.seg, X)
newNode.left = l
newNode.right = r
return newNode
}
if isAbove(newNode.seg, node.seg, X) {
node.right = insertNode(node.right, newNode, X)
} else {
node.left = insertNode(node.left, newNode, X)
}
return node
}
func removeNode(node *Node, seg Segment, X int64) *Node {
if node == nil {
return nil
}
if node.seg.id == seg.id {
return merge(node.left, node.right)
}
if isAbove(seg, node.seg, X) {
node.right = removeNode(node.right, seg, X)
} else {
node.left = removeNode(node.left, seg, X)
}
return node
}
func findAbove(root *Node, X int64, Y int64) *Segment {
var best *Segment
curr := root
for curr != nil {
num, den := evalAt(curr.seg, X)
if Y*den < num {
best = &curr.seg
curr = curr.left
} else {
curr = curr.right
}
}
return best
}
func findBelow(root *Node, X int64, Y int64) *Segment {
var best *Segment
curr := root
for curr != nil {
num, den := evalAt(curr.seg, X)
if Y*den > num {
best = &curr.seg
curr = curr.right
} else {
curr = curr.left
}
}
return best
}
func isInside(A, B, D Point) bool {
cross := A.x*B.y - A.y*B.x
if cross > 0 {
return (A.x*D.y-A.y*D.x) > 0 && (D.x*B.y-D.y*B.x) > 0
} else if cross < 0 {
return (A.x*D.y-A.y*D.x) > 0 || (D.x*B.y-D.y*B.x) > 0
}
return (-B.x*D.y + B.y*D.x) > 0
}
type EventGroup struct {
ending []Segment
starting []Segment
verts []int
}
type TempEdge struct {
u, v int
w float64
directed bool
}
type Edge struct {
to int
w float64
}
type Item struct {
id int
dist float64
index int
}
type PriorityQueue []*Item
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].dist < pq[j].dist }
func (pq PriorityQueue) Swap(i, j int) {
pq[i], pq[j] = pq[j], pq[i]
pq[i].index = i
pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
n := len(*pq)
item := x.(*Item)
item.index = n
*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
item.index = -1
*pq = old[0 : n-1]
return item
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
buf := make([]byte, 0, 10*1024*1024)
scanner.Buffer(buf, 10*1024*1024)
scanner.Split(bufio.ScanWords)
nextInt := func() int {
scanner.Scan()
res, _ := strconv.Atoi(scanner.Text())
return res
}
if !scanner.Scan() {
return
}
N, _ := strconv.Atoi(scanner.Text())
V := make([]Point, N)
for i := 0; i < N; i++ {
V[i] = Point{id: i, x: int64(nextInt()), y: int64(nextInt())}
}
s := nextInt() - 1
t := nextInt() - 1
segs := make([]Segment, N)
for i := 0; i < N; i++ {
p1, p2 := V[i], V[(i+1)%N]
if p1.x > p2.x {
p1, p2 = p2, p1
}
if p1.x < p2.x {
segs[i] = Segment{id: i, P1: p1, P2: p2, dx: p2.x - p1.x, dy: p2.y - p1.y}
} else {
segs[i] = Segment{id: i, P1: p1, P2: p2, dx: 0, dy: p2.y - p1.y}
}
}
uniqueX := make([]int64, 0)
xMap := make(map[int64]bool)
events := make(map[int64]*EventGroup)
for i := 0; i < N; i++ {
x := V[i].x
if !xMap[x] {
xMap[x] = true
uniqueX = append(uniqueX, x)
events[x] = &EventGroup{}
}
events[x].verts = append(events[x].verts, i)
seg := segs[i]
if seg.dx > 0 {
events[seg.P1.x].starting = append(events[seg.P1.x].starting, seg)
events[seg.P2.x].ending = append(events[seg.P2.x].ending, seg)
}
}
sort.Slice(uniqueX, func(i, j int) bool { return uniqueX[i] < uniqueX[j] })
var root *Node
nextID := N
nodes := make([]NodeData, 0, 3*N)
for i := 0; i < N; i++ {
nodes = append(nodes, NodeData{id: i, x: float64(V[i].x), y: float64(V[i].y)})
}
segmentPoints := make([][]int, N)
for i := 0; i < N; i++ {
segmentPoints[i] = append(segmentPoints[i], i, (i+1)%N)
}
var allEdges []TempEdge
addEdge := func(u, v int, w float64, directed bool) {
allEdges = append(allEdges, TempEdge{u, v, w, directed})
}
for _, X := range uniqueX {
eg := events[X]
for _, seg := range eg.ending {
root = removeNode(root, seg, X)
}
sort.Slice(eg.verts, func(a, b int) bool {
return V[eg.verts[a]].y < V[eg.verts[b]].y
})
vertsAtX := make([]Point, len(eg.verts))
for idx, vID := range eg.verts {
vertsAtX[idx] = Point{id: vID, x: V[vID].x, y: V[vID].y}
}
for idx, vID := range eg.verts {
vPt := V[vID]
A := Point{x: V[(vID+1)%N].x - vPt.x, y: V[(vID+1)%N].y - vPt.y}
B := Point{x: V[(vID-1+N)%N].x - vPt.x, y: V[(vID-1+N)%N].y - vPt.y}
if isInside(A, B, Point{x: 0, y: 1}) {
cand1Valid := idx+1 < len(vertsAtX)
cand2 := findAbove(root, X, vPt.y)
if cand1Valid && cand2 != nil {
num, den := evalAt(*cand2, X)
if vertsAtX[idx+1].y*den < num {
addEdge(vertsAtX[idx+1].id, vPt.id, float64(vertsAtX[idx+1].y-vPt.y), true)
} else {
hitY := float64(num) / float64(den)
pID := nextID
nextID++
nodes = append(nodes, NodeData{pID, float64(X), hitY})
segmentPoints[cand2.id] = append(segmentPoints[cand2.id], pID)
addEdge(pID, vPt.id, hitY-float64(vPt.y), true)
}
} else if cand1Valid {
addEdge(vertsAtX[idx+1].id, vPt.id, float64(vertsAtX[idx+1].y-vPt.y), true)
} else if cand2 != nil {
num, den := evalAt(*cand2, X)
hitY := float64(num) / float64(den)
pID := nextID
nextID++
nodes = append(nodes, NodeData{pID, float64(X), hitY})
segmentPoints[cand2.id] = append(segmentPoints[cand2.id], pID)
addEdge(pID, vPt.id, hitY-float64(vPt.y), true)
}
}
if isInside(A, B, Point{x: 0, y: -1}) {
cand1Valid := idx-1 >= 0
cand2 := findBelow(root, X, vPt.y)
if cand1Valid && cand2 != nil {
num, den := evalAt(*cand2, X)
if vertsAtX[idx-1].y*den > num {
addEdge(vPt.id, vertsAtX[idx-1].id, float64(vPt.y-vertsAtX[idx-1].y), true)
} else {
hitY := float64(num) / float64(den)
pID := nextID
nextID++
nodes = append(nodes, NodeData{pID, float64(X), hitY})
segmentPoints[cand2.id] = append(segmentPoints[cand2.id], pID)
addEdge(vPt.id, pID, float64(vPt.y)-hitY, true)
}
} else if cand1Valid {
addEdge(vPt.id, vertsAtX[idx-1].id, float64(vPt.y-vertsAtX[idx-1].y), true)
} else if cand2 != nil {
num, den := evalAt(*cand2, X)
hitY := float64(num) / float64(den)
pID := nextID
nextID++
nodes = append(nodes, NodeData{pID, float64(X), hitY})
segmentPoints[cand2.id] = append(segmentPoints[cand2.id], pID)
addEdge(vPt.id, pID, float64(vPt.y)-hitY, true)
}
}
}
for _, seg := range eg.starting {
newNode := &Node{seg: seg, priority: rand.Int()}
root = insertNode(root, newNode, X)
}
}
for i := 0; i < N; i++ {
pts := segmentPoints[i]
sort.Slice(pts, func(a, b int) bool {
na, nb := nodes[pts[a]], nodes[pts[b]]
if math.Abs(na.x-nb.x) > 1e-9 {
return na.x < nb.x
}
return na.y < nb.y
})
for j := 0; j < len(pts)-1; j++ {
u, v := pts[j], pts[j+1]
nu, nv := nodes[u], nodes[v]
dx := nu.x - nv.x
dy := nu.y - nv.y
dist := math.Sqrt(dx*dx + dy*dy)
addEdge(u, v, dist, false)
}
}
adj := make([][]Edge, nextID)
for _, e := range allEdges {
adj[e.u] = append(adj[e.u], Edge{e.v, e.w})
if !e.directed {
adj[e.v] = append(adj[e.v], Edge{e.u, e.w})
}
}
dist := make([]float64, nextID)
for i := range dist {
dist[i] = math.Inf(1)
}
dist[s] = 0
pq := make(PriorityQueue, 0)
heap.Push(&pq, &Item{id: s, dist: 0})
for pq.Len() > 0 {
curr := heap.Pop(&pq).(*Item)
u := curr.id
if curr.dist > dist[u] {
continue
}
if u == t {
break
}
for _, edge := range adj[u] {
if dist[u]+edge.w < dist[edge.to] {
dist[edge.to] = dist[u] + edge.w
heap.Push(&pq, &Item{id: edge.to, dist: dist[edge.to]})
}
}
}
fmt.Printf("%.9f\n", dist[t])
}
```