题目链接

BZOJ2395

题意:无向图中每条边有两种权值,定义一个生成树的权值为两种权值各自的和的积

求权值最小的生成树

题解

如果我们将一个生成树的权值看做坐标,那么每一个生成树就对应一个二维平面上的坐标

在同一个反比例函数图像上的点权值相同,反比例函数\(xy\)越小的点越贴近坐标轴

所以答案一定在下凸包上

我们就递归查找这样的点

我们先分别将两种权值作为指标求出\(A\)和\(B\)两个点,分别是\(x\)最小的点和\(y\)最小的点,即为下凸包的一个边界

我们找到位于\(AB\)左下角最远的点\(C\)

为了方便,由于底\(|AB|\)确定,\(S\triangle ABC\)越大,距离越远

那么\(C\)满足最小化

\[\overrightarrow{CA} \times \overrightarrow{CB}
\]

展开叉乘,去掉常数项,可得

\[(y_A - y_B) * x_C + (x_B - x_A) * y_C
\]

将其作为新的边权,跑kruskal即可得到新的点\(C\)

然后将\(AC\)和\(CB\)分别作为底继续递归下去,直至找不到点为止

过程中更新答案,显然一定会经过下凸包上所有点

复杂度\(O(可以被卡)\),只需要构造所有点都在下凸包上,就会退化为\(O(生成树个数)\)

不知道能不能构造出来

为了方便理解,再盗一张图

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 205,maxm = 10005,INF = 0x3fffffff;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int n,m,pre[maxn];
struct EDGE{int a,b,x,y,v;}e[maxm];
struct point{int x,y;}ans;
inline int find(int u){return u == pre[u] ? u : pre[u] = find(pre[u]);}
inline bool operator <(const EDGE& a,const EDGE& b){
return a.v < b.v;
}
inline point operator -(const point& a,const point& b){
return (point){a.x - b.x,a.y - b.y};
}
inline LL operator *(const point& a,const point& b){
return 1ll * a.x * b.y - 1ll * a.y * b.x;
}
inline bool operator <(const point& a,const point& b){
return 1ll * a.x * a.y == 1ll * b.x * b.y ? a.x < b.x : 1ll * a.x * a.y < 1ll * b.x * b.y;
}
point kruskal(){
sort(e + 1,e + 1 + m);
REP(i,n) pre[i] = i;
point re; int cnt = n,u,v; re.x = re.y = 0;
for (int i = 1; i <= m && cnt > 1; i++){
u = find(e[i].a); v = find(e[i].b);
if (u != v){
pre[u] = v;
cnt--;
re.x += e[i].x;
re.y += e[i].y;
}
}
if (re < ans) ans = re;
return re;
}
void solve(point A,point B){
REP(i,m) e[i].v = (A.y - B.y) * e[i].x + (B.x - A.x) * e[i].y;
point C = kruskal();
if ((C - A) * (B - A) <= 0) return;
solve(A,C); solve(C,B);
}
int main(){
n = read(); m = read(); ans.x = ans.y = INF;
REP(i,m){
e[i].a = read() + 1,e[i].b = read() + 1;
e[i].x = read(),e[i].y = read();
}
REP(i,m) e[i].v = e[i].x;
point A = kruskal();
REP(i,m) e[i].v = e[i].y;
point B = kruskal();
solve(A,B);
printf("%d %d\n",ans.x,ans.y);
return 0;
}

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

  1. bzoj2395[Balkan 2011]Timeismoney最小乘积生成树

    所谓最小乘积生成树,即对于一个无向连通图的每一条边均有两个权值xi,yi,在图中找一颗生成树,使得Σxi*Σyi取最小值. 直接处理问题较为棘手,但每条边的权值可以描述为一个二元组(xi,yi),这也 ...

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

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

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

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

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

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

  5. bzoj2395: [Balkan 2011]Timeismoney

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

  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. git 修改commit 的注释

    git 修改commit 的注释 一:最新的一次提交 当你不小心,写错了提交的注视/信息,该如何处理呢.理论上,SCM是不应该修改历史的信息的,提交的注释也是.    不过在git中,其commit提 ...

  2. 一些常用的集合工具的代码块(缓慢更新XD,更新了多属性过滤:) )

    更新记录 虽然经常放鸽子,但是还是要记录一下更新 2017.8.30 更新了listToMap的方法,现在可以指定多个属性进行分组了,例如你要指定一个学生集合,按照名字和年龄相同的放在一组,现在只要调 ...

  3. C# StreamReader对象

    1.读取文件 输入流用于从外部源读取数据,在很多情况下,数据源可以是磁盘上的文件或网络的某些位置,任何可能发送数据的位置都可以是数据源,比如网络应用程序,web服务,甚至是控制台.StreamRead ...

  4. 架构图(拓扑图)画图工具分析整理(静态,动态,可交互图.层级tu)

    最近要画架构图. 一方面有图片洁癖,另外一方面又不想不停的挪动图片. 一开始想用脑图软件. 发现脑图是树状的,架构模块依赖图是网状的.(也可以简化为层级图,不画交互关系.类似 dubbo 的架构图. ...

  5. mysql 存储过程 例子

    DROP PROCEDURE IF EXISTS variable_demo; delimiter // CREATE PROCEDURE variable_demo() BEGIN select ' ...

  6. linux下vim命令

    进入vim的命令 vim filename :打开或新建文件,并将光标置于第一行首 vim +n filename :打开文件,并将光标置于第n行首 vim + filename :打开文件,并将光标 ...

  7. 二 python并发编程之多进程-理论

    一 什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 举例(单核+多道,实现多个进程的并发执行): egon在一个时间段内有很多任务要做:python备课的任务,写书的任 ...

  8. h5获取摄像头拍照功能

    完整代码展示 <!DOCTYPE html> <head> <title>HTML5 GetUserMedia Demo</title> <met ...

  9. pandas库Series类型与基本操作

    pandas读取excel的类型是dataFrame,然后提取每一列是一个Series类型 Series类型包括index和values两部分 a = pd.Series({'a':1,'b':5}) ...

  10. 网络编程协议(TCP和UDP协议,粘包问题)以及socketserver模块

    网络编程协议 1.osi七层模型 应用层  表示层  会话层  传输层  网络层  数据链路层  物理层 2.套接字 socket 有两类,一种基于文件类型,一种基于网络类型 3.Tcp和udp协议 ...