码迷,mamicode.com
首页 > 其他好文 > 详细

#3409. 小P的生成树(mst)

时间:2020-03-01 21:57:19      阅读:75      评论:0      收藏:0      [点我收藏+]

标签:scan   scanf   最大   之间   i++   pac   关系   ret   col   

题目描述

小`P`时勤于思考的好孩子,自从学习了最大生成树后,他就一直想:能否将边权范围从实数推广到复数呢?可是马上小`P`就发现了问题,复数之间的大小关系并没有定义。于是对于任意两个复数 $z_1,z_2$ ,小`P`定义 $z_1<z_2$ 当且仅当 $|z_1|<|z_2|$ 。

现在,给出一张 $n$ 个点 $m$ 条边的简单无向带权图,小`P`想问你,如果按照他对复数大小的定义,这个图的最大生成树是什么?

题解

好像在杭州的时候讲过这题,答案是一个复数,放在复平面上的话是个向量,所以如果确定方向的话我们就是把所有向量在这个平面上做投影,要使得投影的和最大去做最大生成树。

可惜角度非常多,那我们可以做最大生成树的时候我们只需要确定两条边的大小关系,不需要只要他具体的值。所以我们可以考虑到两条向量相等的时候角度是确定的,所以我们把这些角度记录下来后排序,然后对于两角直接的方向边的大小关系都是一样的,所以任选一个方向去投影,做最大生成树即可。

效率: $O(m^3logm)$ (有排序)。

代码

#include <bits/stdc++.h>
#define db double
using namespace std;
const db p=acos(-1);
int n,m,t,u[205],v[205],fa[55];
db a[205],b[205],f[40005],ans;
struct O{int x,y;db i,j,z;}g[205];
int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);}
bool cmp(O A,O B){return A.z>B.z;}
db work(db w){
    db X=sin(w),Y=cos(w),U=0,V=0;
    for (int i=1;i<=m;i++)
        g[i]=(O){u[i],v[i],a[i],b[i],b[i]*X+a[i]*Y};
    sort(g+1,g+m+1,cmp);
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int x,y,i=1;i<=m;i++){
        x=get(g[i].x);y=get(g[i].y);
        if (x!=y) fa[x]=y,U+=g[i].i,V+=g[i].j;
    }
    return U*U+V*V;
}
int main(){
    cin>>n>>m;
    for (int i=1;i<=m;i++)
        scanf("%d%d%lf%lf",&u[i],&v[i],&a[i],&b[i]);
    for (int i=1;i<m;i++)
        for (int j=i+1;j<=m;j++){
            if (b[i]==b[j]) f[++t]=-p/2;
            else f[++t]=atan((a[i]-a[j])/(b[i]-b[j]));
            f[t+1]=f[t]+p;t++;
        }
    f[++t]=-p/2;f[++t]=p*5/2;
    sort(f+1,f+t+1);t=unique(f+1,f+t+1)-f-1;
    for (int i=1;i<=t;i++)
        ans=max(ans,work((f[i]+f[i-1])/2));
    printf("%.6lf\n",sqrt(ans));return 0;
}

 

#3409. 小P的生成树(mst)

标签:scan   scanf   最大   之间   i++   pac   关系   ret   col   

原文地址:https://www.cnblogs.com/xjqxjq/p/12392122.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!