您的位置:首页 > 其它

【bzoj 十连测】[noip2016十连测第九场]Problem C: 小P的生成树(kruskal+数学知识)

2016-11-17 10:09 405 查看






【题解】【kruskal】

【因为要使生成树的边长和模长最大,那么只需每个复数在其方向向量的投影最大。so,可以把投影作为权值来做最大生成树。】

【kruskal的理论告诉我们:生成树的大小只与各边的相对边长有关,也就是说,只需考虑各边的大小关系尽量选权值大的边建生成树就行】

【考虑两边权值:x1+yi1和x2+yi2(y1≠y2),当两边的复数的投影相等时,方向向量需要满足:x1cosθ+y1sinθ=x2cosθ+y2sinθ,化简后tanθ1=y2−y1x1−x2,θ2=arctany2−y1x1−x2+π;而当x1≠x2,y1=y2时,等式化为x1cosθ=x2cosθ=>cosθ=0=>θ1=π2,θ2=3π2】

【枚举每一对边,算出两条边投影相等时极角的分界点,此时会把区间[−π2,3π2]分为若干个小区间,由于每条边的投影大小关于极角的变化是连续的,所以在每个小区间里权值的大小关系不发生改变。】

【枚举每个区间,做最大生成树】

[复数处理时,和实数是相似的]

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
int x,y;
double a,b,way;
}d[410];
int fa[110],cnt,n,m;
double ans,f[80010];
int tmp(node p,node q)
{
return p.way>q.way;
}
int find(int x)
{
if(fa[x]==x) return fa[x];
fa[x]=find(fa[x]);
return fa[x];
}
inline double shorter(double x)
{
double cs=cos(x); double sn=sin(x);
for(int i=1;i<=m;++i) d[i].way=cs*d[i].a+sn*d[i].b;
sort(d+1,d+m+1,tmp);
for(int i=1;i<=n;++i)fa[i]=i;
double A=0,B=0;
for(int i=1,cnt=1;i<=m;++i)
{
int l1=find(d[i].x),l2=find(d[i].y);
if(l1!=l2)
{
A+=d[i].a; B+=d[i].b; fa[l1]=l2;
++cnt;
if(cnt==n) return (A*A+B*B);
}
}
}
inline void solve()
{
cnt=0; ans=-1;
for(int i=1;i<m;++i)
for(int j=i+1;j<=m;++j)
{
double x1=d[i].a,y1=d[i].b,x2=d[j].a,y2=d[j].b;
if(y1>y2) swap(x1,x2),swap(y1,y2);
if(y1==y2) {f[++cnt]=M_PI/2; f[++cnt]=-M_PI/2; continue;}
f[++cnt]=atan((x1-x2)/(y2-y1));
f[cnt+1]=f[cnt]+M_PI; cnt++;
}
f[++cnt]=-M_PI/2; f[++cnt]=M_PI*5/2;
sort(f+1,f+cnt+1);
cnt=unique(f+1,f+cnt+1)-f-1;
for(int i=1;i<=cnt;++i) ans=max(ans,shorter((f[i]+f[i-1])/2));
if(ans<0) printf("-1\n");
else printf("%.6lf\n",sqrt(ans));
return;
}
int main()
{
freopen("int.txt","r",stdin);
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)scanf("%d%d%lf%lf",&d[i].x,&d[i].y,&d[i].a,&d[i].b);
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kruskal 数学知识
相关文章推荐