2017.08.19【NOIP提高组】模拟赛B组 【雅礼联考GDOI2017模拟】Zjr506的捕猫计划

####Description

Zjr506很喜欢猫,某一天他突然心血来潮,想捕捉学校里活动的猫。
为了捕猫,Zjr506在校园中放置了N个木桩,当他见到有猫进入他的狩猎范围后,就会以迅雷不及掩耳的速度在一些木桩之间绕上藩篱以困住这些猫。
一段时间后,Zjr506在绕了M个藩篱后兴高采烈的离开了。作为正义的使者,Ztxz16不忍心看到这些猫受到折磨,于是决定拆除一些藩篱让所有的猫都逃出去。因为Zjr506的巧妙设计,藩篱不会在除木桩之外的地方相交。这些藩篱构成了一些封闭的区域,每一个区域中都有一只猫。
因为Zjr506制造这些藩篱也不容易,所以Ztxz16希望拆除的藩篱总长度尽量小,现在他希望你告诉他最小的总长度。

####Input

第一行两个数 n,m ( 2<=n<=10000 , 1<=m<=50000 )
接下来 n 行 , 每行两个整数 xi,yi, 代表第 i 个木桩的坐标 ( − 10000 ≤ xi, yi ≤ 10000).
接下来 m 行,每行两个整数 pi,qi (1 ≤ pj, qj ≤ N) , 代表木桩 pi 与木桩 qi 之间有一个藩篱。

####Output

输出一个实数代表答案,当答案与标准答案差的绝对值不超过0.001时认为它是正确的。

####Sample Input

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

####Sample Output

4.000000000

####Data Constraint

对于20%的数据:
N , M <= 30
对于30%的数据:
N, M <= 300
对于100%的数据:
N <= 10000, M <= 50000

###题解
首先,看到题目以为是什么暴力搜索或是什么玄学的东东,但是转念一看,发现是要删边,再认真看看,原来是给出很多条边,然后去掉比较小的边,让整个图没有环。
正着去看,发现去掉小边比较麻烦,那么就反着来看。
于是本题就变成了求最大生成树。(很类似)
我们怎么做呢?
首先先排个序,然后一条一条边加入图中,若是当前边加入了之后就出现环,那么就不加入。答案就把这条边的距离加上。用题目的话来说就是把这条栏杆给去掉。
正确性?
因为当前边是当前环(连上了之后)最小的边,去掉其他的边不是更优的,所以去掉当前边是最优解。
那么如何判断是一个环?就直接用一个简单的并查集即可。

Code:

type
        new=record
                x:longint;
                y:longint;
                long:extended;
        end;
var
        f:array[1..100001] of longint;
        a:array[0..100001] of new;
        e:array[0..100001,1..2] of longint;
        i,j,k,n,m,x,y,c:longint;
        ans:extended;
procedure qsort(l,r:longint);
var
        x,y:longint;
        m:extended;
begin
        x:=l;
        y:=r;
        m:=a[(l+r) div 2].long;
        repeat
                while a[x].long>m do inc(x);
                while a[y].long<m do dec(y);
                if x<=y then
                begin
                        a[0]:=a[x];
                        a[x]:=a[y];
                        a[y]:=a[0];
                        inc(x);
                        dec(y);
                end;
        until x>y;
        if l<y then qsort(l,y);
        if r>x then qsort(x,r);
end;
function fu(z:longint):longint;
var
        x,y:longint;
begin
        y:=z;
        while y<>f[y] do y:=f[y];
        while z<>y do
        begin
                x:=f[z];
                f[z]:=y;
                z:=x;
        end;
        exit(y);
end;
begin
        readln(n,m);
        for i:=1 to n do
        begin
                readln(x,y);
                e[i,1]:=x;
                e[i,2]:=y;
        end;
        for i:=1 to m do
        begin
                readln(x,y);
                a[i].x:=x;a[i].y:=y;
                a[i].long:=sqrt(sqr(e[x,1]-e[y,1])+sqr(e[x,2]-e[y,2]));
        end;
        qsort(1,m);
        for i:=1 to n do f[i]:=i;
        ans:=0;
        for i:=1 to m do
        begin
                j:=fu(a[i].x);
                k:=fu(a[i].y);
                if j<>k then
                begin
                        f[j]:=k;
                end
                else
                begin
                        ans:=ans+a[i].long;
                end;
        end;
        writeln(ans:0:9);
end.


我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
原文地址:https://www.cnblogs.com/RainbowCrown/p/11148419.html