Problem:


  MathJax.Hub.Config({ tex2jax: { inlineMath: [["$","$"], ["\\(","\\)"]], processEscapes: true }});

村の道路計画 

会津国の若松平野に、集落が点在していました。いくつかの集落の間はまっすぐで互いに行き来できる道で繋がっていて、平野にあるどの集落の間も道を辿って行き来ができます。それぞれの道には長さに応じた維持費がかかりますが、すべての集落が資金を出し合って道を維持していました。

あるとき、すべての集落が一つの村にまとまることが決まり、村を囲む境界線を引くことになりました。国の決まりでは、村を構成するどの２つの集落を結んだまっすぐな線も、村の外を通ってはいけません（境界線上を通ることは許されます）。さらに、会津国では村を囲む境界線上に道がなければなりません。境界線上に道がない場所には、国が新たに道を作ってくれます。

しかし、道の維持費は村が支払うので、村人達は境界線をできるだけ短くしたいと考えています。さらに、村人達はすべての集落の間を行き来できる状態を維持しつつ、境界線上にない道を廃止することで、道の長さの合計を最小にすることにしました。

集落の位置と元々あった道の情報が与えられる。境界線上に道を置き、かつ、すべての集落が行き来できるようにした場合の、道の長さの合計の最小値を計算するプログラムを作成せよ。ただし、集落は大きさのない点、道は幅のない線分とする。 

Input

入力は以下の形式で与えられる。 

V R
x1 y1
x2 y2
:
xV yV
s1 t1
s2 t2
:
sR tR

１行目に集落の数 V (3 ≤ V ≤ 100) と道の数 R (2 ≤ R ≤ 1000) が与えられる。

続く V 行に集落を表す点の情報が与えられる。各行に与えられる２つの整数 xi, yi (-1000 ≤ xi, yi ≤ 1000)は、それぞれ i 番目の集落の x 座標、y座標を表す。

続く R 行に元々あった道の情報が与えられる。各行に与えられる２つの整数 si, ti (1 ≤ si < ti ≤ V)は、i 番目の道が si 番目の集落と ti 番目の集落をつないでいることを表す。

入力は以下の条件を満たす。

 i &ne; j ならば、i 番目の集落と j 番目の集落の座標は異なる。

 どの２つの集落についても、それらを直接結ぶ道は高々１つしか現れない。

２つの異なる道が端点以外の点を共有することはない。

 ３つ以上の集落が同一直線上に並んでいることはない。 

Output

条件を満たす道の長さの合計の最小値を１行に実数で出力する。ただし、誤差がプラスマイナス 0.001 を超えてはならない。この条件を満たせば小数点以下何桁表示してもよい。 

Sample Input 1

5 5
0 0
1 1
3 0
3 2
0 2
1 2
2 3
2 4
3 4
1 5 

Sample Output 1

11.4142

Sample Input 2

7 6
0 2
3 0
2 2
1 0
4 1
2 3
3 5
1 3
2 4
2 3
3 5
3 7
6 7 

Sample Output 2

18.2521



Sample input:
5 5
0 0
1 1
3 0
3 2
0 2
1 2
2 3
2 4
3 4
1 5


Sample output:
11.4142

Submission status: Accepted

Solution code:
def dist2(A, B):
    return (A[0]-B[0])**2 + (A[1]-B[1])**2
def cross(A, B, C):
    return (B[0]-A[0])*(C[1]-A[1]) - (C[0]-A[0])*(B[1]-A[1])
def convex_hull(PS):
    n = len(PS)
    PP = sorted([p + [i] for i, p in enumerate(PS)])
    Q = []
    for i in range(n):
        while len(Q) > 1 and cross(PP[Q[-2]], PP[Q[-1]], PP[i]) >= 0:
            Q.pop()
        Q.append(i)
    k = len(Q)
    for i in range(n-2, -1, -1):
        while len(Q) > k and cross(PP[Q[-2]], PP[Q[-1]], PP[i]) >= 0:
            Q.pop()
        Q.append(i)
    return list(PP[i][2] for i in Q)

from math import sqrt
v, r = map(int, input().split())
PS = [list(map(int, input().split())) for i in range(v)]
CH = convex_hull(PS)

*parent, = range(v)
def root(x):
    if x == parent[x]:
        return x
    parent[x] = root(parent[x])
    return parent[x]
def unite(x, y):
    px = root(x); py = root(y)
    if px < py:
        parent[py] = px
    else:
        parent[px] = py

ans = 0
for i in range(len(CH)-1):
    unite(CH[i], CH[i+1])
    ans += sqrt(dist2(PS[CH[i]], PS[CH[i+1]]))
R = [list(map(int, input().split())) for i in range(r)]
R.sort(key=lambda x: dist2(PS[x[0]-1], PS[x[1]-1]))
for s, t in R:
    if root(s-1) != root(t-1):
        unite(s-1, t-1)
        ans += sqrt(dist2(PS[s-1], PS[t-1]))
print(ans)

Submission status: Accepted

Solution code:
def quickhull(l,r,s,k,il,ir):
    if not s:
        return
    su = []
    sd = []
    a = (r[0]-l[0],r[1]-l[1])
    for x,y in s:
        b = (x-l[0],y-l[1])
        cro = cross(a,b)
        if cro > 0:
            su.append((x,y))
        elif cro < 0:
            sd.append((x,y))

    ind = (ir-il)/2

    if su:
        c,d = direction(l,r,su[0])
        p = su[0]
        for i in range(1,len(su)):
            c_,d_ = direction(l,r,su[i])
            if c*d_ < c_*d:
                c,d = c_,d_
                p = su[i]
        i = ir+ind
        k.append((tuple(p),i))
        b = (l[0]-p[0],l[1]-p[1])
        c = (p[0]-r[0],p[1]-r[1])
        s1 = []
        s2 = []
        for x,y in su:
            b_ = (x-p[0],y-p[1])
            c_ = (x-r[0],y-r[1])
            cro_b,cro_c = cross(b,b_),cross(c,c_)
            if cro_b >= 0 and cro_c >= 0:
                continue
            else:
                if cro_b < 0:
                    s1.append((x,y))
                elif cro_c < 0:
                    s2.append((x,y))
        quickhull(l,p,s1,k,il,i)
        quickhull(r,p,s2,k,ir,i)

    if sd:
        c,d = direction(l,r,sd[0])
        p = sd[0]
        for i in range(1,len(sd)):
            c_,d_ = direction(l,r,sd[i])
            if c*d_ < c_*d:
                c,d = c_,d_
                p = sd[i]
        i = il+ind
        k.append((tuple(p),i))
        b = (l[0]-p[0],l[1]-p[1])
        c = (p[0]-r[0],p[1]-r[1])
        s1 = []
        s2 = []
        for x,y in sd:
            b_ = (x-p[0],y-p[1])
            c_ = (x-r[0],y-r[1])
            cro_b,cro_c = cross(b,b_),cross(c,c_)
            if cro_b <= 0 and cro_c <= 0:
                continue
            else:
                if cro_b > 0:
                    s1.append((x,y))
                elif cro_c > 0:
                    s2.append((x,y))
        quickhull(l,p,s1,k,il,i)
        quickhull(p,r,s2,k,i,ir)
    k.sort(key = lambda x:x[1])
    return tuple(zip(*k))[0]

def cross(a,b):
    return a[0]*b[1]-a[1]*b[0]

def direction(l,r,p):
    a = r[1]-l[1]
    b = l[0]-r[0]
    return (a*(p[0]-l[0])+b*(p[1]-l[1]))**2, a**2+b**2
def root(x):
    if par[x] == x:
        return x
    par[x] = root(par[x])
    return par[x]

def unite(x,y):
    x = root(x)
    y = root(y)
    if rank[x] < rank[y]:
        par[x] = y
    else:
        par[y] = x
        if rank[x] == rank[y]:
            rank[x] += 1

from collections import defaultdict
n,r = [int(x) for x in input().split()]
s = [[int(x) for x in input().split()] for i in range(n)]
f = defaultdict(int)
for i in range(n):
    x,y = s[i]
    f[(x,y)] = i
q = [[s[i][j] for j in range(2)] for i in range(n)]
q.sort()
v = []
for i in range(r):
    a,b = [int(x) for x in input().split()]
    a -= 1
    b -= 1
    c = ((s[a][0]-s[b][0])**2+(s[a][1]-s[b][1])**2)**0.5
    v.append((a,b,c))
v.sort(key = lambda x:x[2])
l = tuple(q.pop(0))
r = tuple(q.pop(-1))
lis = quickhull(l,r,q,[(l,0),(r,n)],0,n)
par = [i for i in range(n+1)]
rank = [0]*(n+1)
for p in lis:
    par[f[p]] = n
    rank[n] = 1
l = 0
for i in range(len(lis)):
    l += ((lis[i-1][0]-lis[i][0])**2+(lis[i-1][1]-lis[i][1])**2)**0.5
for x,y,c in v:
    if root(x) != root(y):
        l += c
        unite(x,y)

print(l)

Submission status: Accepted

Solution code:
import sys


def graham_scan(vertices: list):
    from math import atan2
    from operator import itemgetter

    vertices.sort(key=itemgetter(1))
    base_x, base_y = vertices[0][:2]
    vertices = sorted(vertices[1:], key=lambda p: atan2(p[1]-base_y, p[0]-base_x)) + vertices[:1]
    convex = vertices[-1:] + vertices[:1]
    pop, append = convex.pop, convex.append
    last_x, last_y = convex[-1][:2]

    for point, (x, y, *_) in zip(vertices[1:], vertices[1:]):
        while ((x - last_x) * (convex[-2][1] - last_y) -
               (y - last_y) * (convex[-2][0] - last_x) < 0):
            pop()
            last_x, last_y = convex[-1][:2]
        append(point)
        last_x, last_y = x, y

    return convex[:-1]


def kruskal(v_count: int, edges: list, border) -> int:
    """
    :param v_count: 頂点数
    :param edges: [(weight, from, to), ... ]
    """
    from itertools import islice
    tree = [-1]*v_count

    def get_root(x) -> int:
        if tree[x] < 0:
            return x
        tree[x] = get_root(tree[x])
        return tree[x]

    def unite(a) -> bool:
        x, y = get_root(a[1]), get_root(a[2])
        if x != y:
            big, small = (x, y) if tree[x] < tree[y] else (y, x)
            tree[big] += tree[small]
            tree[small] = big
        return x != y

    cost = 0
    for c, s, t in border_edges:
        cost += c
        unite((0, s, t))
    v_count -= len(border_edges)-1
    for w, _s, _t in islice(filter(unite, sorted(edges)), v_count-1):
        cost += w
    return cost


if __name__ == "__main__":
    from math import hypot
    V, R = map(int, input().split())
    vertices = [list(map(int, sys.stdin.readline().split()))+[i] for i in range(V)]
    edges = [(hypot(vertices[s-1][0]-vertices[t-1][0], vertices[s-1][1]-vertices[t-1][1]), s-1, t-1)
             for l in sys.stdin for s, t in (map(int, l.split()),)]
    convex = graham_scan(vertices)
    border_edges = [(hypot(x1-x2, y1-y2), s, t) for (x1, y1, s), (x2, y2, t) in zip(convex, convex[1:]+convex[:1])]
    cost = kruskal(V, edges, border_edges)
    print(cost)