所谓最小乘积生成树,即对于一个无向连通图的每一条边均有两个权值xi,yi,在图中找一颗生成树,使得Σxi*Σyi取最小值。

直接处理问题较为棘手,但每条边的权值可以描述为一个二元组(xi,yi),这也就不难想到将生成树转化为平面内的点,x代表Σxi,y代表Σyi(注意这里的xi,yi指的是在生成树中的边的权值),那么问题就变成了在平面内找一个点使得x*y最小,那么显然这个点是在下凸壳上的。

因此可以首先找出两个一定在凸包上的点,例如A(minx,y),B(miny,x),在直线AB下方找一个在凸包上且x*y最小的点。

于是可以每次找距离直线AB最远的点,有两种求法,令找到的那个点为C,如果利用叉乘,即使向量CB叉乘向量CA最大,因为我们考虑的是向量的模长,可以让向量CA叉乘向量CB(虽然模长是负的,但并没有什么关系,当然也可以最大化这个值,只不过一个是最小生成树,一个是最大生成树而已),然后最小化这个值即可。

2S=(B.x-A.x)(C.y-B.y)-(B.y-A.y)(C.x-A.x)省略常数后就变成了B.x*C.y-A.x*C.y-B.y*C.x+A.y*C.x。

因为只需要求Σxi,Σyi,因此只需要求出点的坐标,并不要考虑面积,所以将每条边的yi=yi*(B.x-A.x),xi=xi*(A.y-B.y),以(xi+yi)为关键字排序kruskal()就行了。

然后递归处理,直到叉积大于等于0退出(此时AB下方一定没有点)

也可以利用点到直线的距离公式|Ax0+By0+C|/(√(A2+B2)),省略常数且保证B<=0,那么若点在直线下方,则Ax+By+C>0。

因此可以省略绝对值符号,再省略常数,即使Ax0+By0最大,因此xi=xi*A,yi=yi*B,将(xi+yi)为关键字排序作最大生成树即可,还是按叉积判断(理应也可以看C.x*A+C.y*B<=0就退出,然而狂WA不止。。。。并不知道这是为什么。。。。)

然后这是一道裸题,就可以愉快地切掉了。

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 10100
#define inf 0x7fffffff int n,m;
int val[*maxn],fa[maxn]; struct edge{
int from,to,x,y;
long long z;
}e[maxn]; struct node{
int x,y;
long long calc(){return (long long)x*y;}
}minx,miny,ans; int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
} node kruskal(){
int tot=;node now={,};
for (int i=;i<=n;i++) fa[i]=i;
for (int i=;i<=m;i++){
int a=find(e[i].from),b=find(e[i].to);
if (a!=b){
fa[a]=b;
tot++;
now.x+=e[i].x;
now.y+=e[i].y;
if (tot==n-) break;
}
}
long long tmpa=ans.calc(),tmpb=now.calc();
if (tmpb<tmpa||(tmpb==tmpa&&now.x<ans.x)) ans=now;
return now;
} long long cross(node a,node b,node c){
long long x1=(b.x-a.x),x2=(b.y-a.y),y1=(c.x-a.x),y2=(c.y-a.y);
return (x1*y2-x2*y1);
} bool cmpx(edge a,edge b){return a.x<b.x;}
bool cmpy(edge a,edge b){return a.y<b.y;}
bool cmpz(edge a,edge b){return a.z<b.z;} void solve(node a,node b){
for (int i=;i<=m;i++)
e[i].z=e[i].y*(b.x-a.x)+e[i].x*(a.y-b.y);
sort(e+,e+m+,cmpz);
node t=kruskal();
if (cross(a,b,t)>=) return;
solve(a,t);
solve(t,b);
} int main(){
scanf("%d%d",&n,&m);
ans=(node){inf,inf};
for (int i=,u,v;i<=m;i++) scanf("%d%d%d%d",&u,&v,&e[i].x,&e[i].y),e[i].from=++u,e[i].to=++v;
sort(e+,e+m+,cmpx);
minx=kruskal();
sort(e+,e+m+,cmpy);
miny=kruskal();
//cout<<minx.x<<' '<<minx.y<<' '<<miny.x<<' '<<miny.y<<endl;
solve(minx,miny);
printf("%d %d",ans.x,ans.y);
return ;
}

1

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define maxn 10100
#define inf 0x7fffffff int n,m;
int val[*maxn],fa[maxn]; struct edge{
int from,to,x,y;
long long z;
}e[maxn]; struct node{
int x,y;
long long calc(){return (long long)x*y;}
}minx,miny,ans; int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
} node kruskal(){
int tot=;node now={,};
for (int i=;i<=n;i++) fa[i]=i;
for (int i=;i<=m;i++){
int a=find(e[i].from),b=find(e[i].to);
if (a!=b){
fa[a]=b;
tot++;
now.x+=e[i].x;
now.y+=e[i].y;
if (tot==n-) break;
}
}
long long tmpa=ans.calc(),tmpb=now.calc();
if (tmpb<tmpa||(tmpb==tmpa&&now.x<ans.x)) ans=now;
return now;
} int cross(node a,node b,node c){
int x1=b.x-a.x,y1=b.y-a.y,x2=c.x-a.x,y2=c.y-a.y;
return (x1*y2-x2*y1);
} bool cmpx(edge a,edge b){return a.x<b.x;}
bool cmpy(edge a,edge b){return a.y<b.y;}
bool cmpz(edge a,edge b){return a.z>b.z;} void solve(node a,node b){
int A=b.y-a.y,B=a.x-b.x;
for (int i=;i<=m;i++) e[i].z=e[i].x*A+e[i].y*B;
sort(e+,e+m+,cmpz);
node t=kruskal();
if (cross(a,b,t)<) solve(a,t),solve(t,b);
} int main(){
// freopen("data.in","r",stdin);
// freopen("WA.out","w",stdout);
scanf("%d%d",&n,&m);
ans=(node){inf,inf};
for (int i=,u,v;i<=m;i++) scanf("%d%d%d%d",&u,&v,&e[i].x,&e[i].y),e[i].from=++u,e[i].to=++v;
sort(e+,e+m+,cmpx);
minx=kruskal();
sort(e+,e+m+,cmpy);
miny=kruskal();
//cout<<minx.x<<' '<<minx.y<<' '<<miny.x<<' '<<miny.y<<endl;
solve(minx,miny);
printf("%d %d",ans.x,ans.y);
return ;
}

2

bzoj2395[Balkan 2011]Timeismoney最小乘积生成树的更多相关文章

  1. Bzoj2395: [Balkan 2011]Timeismoney(最小乘积生成树)

    问题描述 每条边两个权值 \(x,y\),求一棵 \((\sum x) \times (\sum y)\) 最小的生成树 Sol 把每一棵生成树的权值 \(\sum x\) 和 \(\sum y\) ...

  2. bzoj 2395 [Balkan 2011]Timeismoney——最小乘积生成树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395 如果把 \( \sum t \) 作为 x 坐标,\( \sum c \) 作为 y ...

  3. 【BZOJ2395】【Balkan 2011】Timeismoney 最小乘积生成树

    链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网 ...

  4. bzoj2395: [Balkan 2011]Timeismoney

    Description      有n个城市(编号从0..n-1),m条公路(双向的),从中选择n-1条边,使得任意的两个城市能够连通,一条边需要的c的费用和t的时间,定义一个方案的权值v=n-1条边 ...

  5. BZOJ2395 [Balkan 2011]Timeismoney 【最小乘积生成树】

    题目链接 BZOJ2395 题意:无向图中每条边有两种权值,定义一个生成树的权值为两种权值各自的和的积 求权值最小的生成树 题解 如果我们将一个生成树的权值看做坐标,那么每一个生成树就对应一个二维平面 ...

  6. bzoj2395 [Balkan 2011]Timeismoney(最小乘积生成树+计算几何)

    题意 每条边有两个权值\(c,t\),请求出一颗生成树,使得\(\sum c\times \sum t\)最小 题解 为什么生成树会和计算几何扯上关系-- 对于每棵树,设\(x=c,y=t\),我们可 ...

  7. 【最小乘积生成树】bzoj2395[Balkan 2011]Timeismoney

    设每个点有x,y两个权值,求一棵生成树,使得sigma(x[i])*sigma(y[i])最小. 设每棵生成树为坐标系上的一个点,sigma(x[i])为横坐标,sigma(y[i])为纵坐标.则问题 ...

  8. P5540-[BalkanOI2011]timeismoney|最小乘积生成树【最小生成树,凸壳】

    正题 题目链接:https://www.luogu.com.cn/problem/P5540 题目大意 给出\(n\)个点\(m\)条边边权是一个二元组\((a_i,b_i)\),求出一棵生成树最小化 ...

  9. 洛谷 P5540 - [BalkanOI2011] timeismoney | 最小乘积生成树(最小生成树)

    洛谷题面传送门 大概是一个比较 trivial 的小 trick?学过了就不要忘了哦( 莫名奇妙地想到了 yyq 的"hot tea 不常有,做过了就不能再错过了" 首先看到这种二 ...

随机推荐

  1. Objective-C面向对象的编程

    Objective-C面向对象的编程 目录 对面向对象编程思想的理解 类的声明和定义 类的声明和定义 对关键字super和self的理解 初始化函数 @property声明类成员 类的实例化 继承 组 ...

  2. cocos2d-x中CCScale9Sprite的另一种实现

    cocos2d 2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观 ...

  3. what is docker

    尽管之前久闻Docker的大名了,但是天资愚钝,对其到底是个啥东西一直摸不清,最近花了一段时间整理了一下,算是整理出一点头绪来. 官网的介绍是这样的: Docker is an open platfo ...

  4. Struts2+hibernate3+Spring2的整合方法

    浅谈Struts+hibernate+Spring的整合方法 摘要:本文将介绍Struts,Spring与hibernate的集成.希望大家能从中受用. 1.在工程中导入spring支持,导入的Jar ...

  5. 1.5.8 语言分析器(Analyzer)

    语言分析器(Analyzer) 这部分包含了分词器(tokenizer)和过滤器(filter)关于字符转换和使用指定语言的相关信息.对于欧洲语言来说,tokenizer是相当直接的,Tokens被空 ...

  6. ProgressBar的Indeterminate属性

    Indeterminate ProgressBar默认是白色的,如果容器的背景也是白色的,这样就根本看不到Progressbar. 简单解决方案: 用style属性设定反转的颜色. <Progr ...

  7. IOS 设置定时器

    IOS 设置定时器  自动滚动视图 定时发送坐标信息 即时显示 时钟 NSTimer *timer; - (void)start {//1second 调用一次 timer = [NSTimer sc ...

  8. IOS 开发中判断NSString是否为空字符

    //当 请求网络 或者获取其他返回数据 首先 要做一次判断 数据是否为空 防止程序崩溃 程序崩溃 好比拿刀扎在程序员的心啊- if(为空) { 做提示对话框等操作 } else { 正常执行 } - ...

  9. Java克隆--深克隆与浅克隆的区别

    克隆,就是复制一个对象的副本,而克隆又分浅克隆和深克隆.浅克隆是指克隆得到的对象基本类型的值改变了,而源对象的值不会变.但如果被克隆对象引用类型的值改变了,那么源对象的值同样会改变,因为引用类型在栈内 ...

  10. foundation框架之反射机制

    概念 例子 一.概念 反射:根据字符串实例话对象或者调用方法 // // main.m // 反射机制 // // Created by apple on 14-3-28. // Copyright ...