正题

题目链接:https://www.luogu.com.cn/problem/P5540


题目大意

给出\(n\)个点\(m\)条边边权是一个二元组\((a_i,b_i)\),求出一棵生成树最小化

\[(\sum_{e\in T}a_e)\times(\sum_{e\in T}b_e)
\]

的情况下最小化\(\sum_{e\in T}a_e\)

\(1\leq n\leq 200,1\leq m\leq 10^4\)


解题思路

这种带乘积的可以维护凸壳,对于一棵生成树\(T\)我们视为一个\((\sum_{e\in T}a_e,\sum_{e\in T}b_i)\)的点,这样我们打答案一定在下凸壳上。

可以用一种分治求凸壳的方法,我们先找出下凸壳的两个端点(\(x\)最小的和\(y\)最小的)记为\(A,B\),然后找到一个在\(A\)与\(B\)的连边下面的一个最凸的点\(C\)(可以视为最大化\(S_{\bigtriangleup ACB}\),这样\(C\)一定在凸壳上),然后分治下去做\(\vec{AC}\)和\(\vec{CB}\)。

考虑怎么求这个\(C\),就是最大化\(\vec{AC}\times \vec{CB}\)

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

然后就是相当于最小化\(x_C(y_B-y_A)+y_C(x_A-x_B)\),拿这个当边权跑就可以跑出\(C\)了。

然后时间复杂度据说是\(O(m\log m\sqrt{\ln n!})\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=210,M=1e4+10;
struct node{
ll x,y,w,id;
}e[M];
struct point{
ll x,y;
point(ll xx=0,ll yy=0)
{x=xx;y=yy;return;}
}ans;
ll n,m,x[M],y[M],a[M],b[M],fa[N];
point operator-(point x,point y)
{return point(x.x-y.x,x.y-y.y);}
ll operator*(point x,point y)
{return x.x*y.y-x.y*y.x;}
bool cmp(node x,node y)
{return (x.w==y.w)?(a[x.id]<a[y.id]):(x.w<y.w);}
ll find(ll x)
{return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
point Kruskal(){
ll cnt=0;point res=0;
for(ll i=1;i<=n;i++)fa[i]=i;
sort(e+1,e+1+m,cmp);
for(ll i=1;i<=m;i++){
ll x=find(e[i].x),y=find(e[i].y);
if(x==y)continue;
fa[x]=y;cnt++;
res.x+=a[e[i].id];
res.y+=b[e[i].id];
if(cnt==n-1)break;
}
if(res.x*res.y<ans.x*ans.y)ans=res;
else if(res.x*res.y==ans.x*ans.y&&res.x<ans.x)
ans=res;
return res;
}
void solve(point A,point B){
for(ll i=1;i<=m;i++)
e[i]=(node){x[i],y[i],(B.x-A.x)*b[i]+(A.y-B.y)*a[i],i};
point C=Kruskal();
if((C-A)*(B-A)<=0)return;
solve(A,C);solve(C,B);
}
signed main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=m;i++){
scanf("%lld%lld%lld%lld",&x[i],&y[i],&a[i],&b[i]);
x[i]++;y[i]++;
}
ans.x=ans.y=1e9;
for(ll i=1;i<=m;i++)e[i]=(node){x[i],y[i],a[i],i};
point A=Kruskal();
for(ll i=1;i<=m;i++)e[i]=(node){x[i],y[i],b[i],i};
point B=Kruskal();
solve(A,B);
printf("%lld %lld\n",ans.x,ans.y);
return 0;
}

P5540-[BalkanOI2011]timeismoney|最小乘积生成树【最小生成树,凸壳】的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. bzoj 2395 Timeismoney —— 最小乘积生成树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395 参考博客:https://www.cnblogs.com/autsky-jadek/p ...

  7. Luogu5540 最小乘积生成树

    Luogu5540 最小乘积生成树 题目链接:洛谷 题目描述:对于一个\(n\)个点\(m\)条边的无向连通图,每条边有两个边权\(a_i,b_i\),求使\((\sum a_i)\times (\s ...

  8. HDU5697 刷题计划 dp+最小乘积生成树

    分析:就是不断递归寻找靠近边界的最优解 学习博客(必须先看这个): 1:http://www.cnblogs.com/autsky-jadek/p/3959446.html 2:http://blog ...

  9. 【算法】最小乘积生成树 & 最小乘积匹配 (HNOI2014画框)

    今天考试的时候果然题目太难于是我就放弃了……转而学习了一下最小乘积生成树. 最小乘积生成树定义: (摘自网上一篇博文). 我们主要解决的问题就是当k = 2时,如何获得最小的权值乘积.我们注意到一张图 ...

随机推荐

  1. mysql行转列 问题 SUM(IF(条件,列值,0))

    sum(if(条件,列值,0))语法用例: select name,sum(if(subject="语文",score,0)) as "语文" from gra ...

  2. C++智能指针的原理和实现

    一.智能指针起因 在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete. new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针: delete:指向 ...

  3. 将svn项目导出,再导入到其他工作空间

    方法一: 对于一致svn地址,本地没有的项目,直接eclipse中svn检出即可. 若本地有项目,但想导入到另一个工作空间(即拷贝一份,不想再从svn拉),则需要用export方法. 方法二(expo ...

  4. 根据经纬度查询最近距离,mysql查询经纬度附近范围

    public class Test{ private static List<LocalAddress> ilist = new ArrayList<LocalAddress> ...

  5. 【MIT6.S081/6.828】手把手教你搭建开发环境

    目录 1. 简介 2. 安装ubuntu20.04 3. 更换源 3.1 更换/etc/apt/sources.list文件里的源 3.2 备份源列表 3.3 打开sources.list文件修改 3 ...

  6. Dockerfile优化——supervisor服务

    一.理解supervisor(supervisor服务不仅在容器中可用,在宿主机中也适用) 1.Dockerfile中的CMD可以指定启动容器后执行的第一个命令,但是当有多个服务进程需要启动的时候,就 ...

  7. python 回归分析

    一.线性回归 1 绘制散点图 import matplotlib.pyplot as plt x = [5,7,8,7,2,17,2,9,4,11,12,9,6] y = [99,86,87,88,1 ...

  8. noip模拟29

    这次终于是早上考试了 早上考试手感不错,这次刷新了以前的最高排名- %%%cyh巨佬 \(rk1\) %%%CT巨佬 \(t2\) 90 纵观前几,似乎我 \(t3\) 是最低的-- 总计挂分10分, ...

  9. Python - 面向对象编程 - 使用 super() 的一些注意事项

    super() 详解 https://www.cnblogs.com/poloyy/p/15223443.html 多继承中使用 super() class A: def test(self): pr ...

  10. dotnet 读 WPF 源代码笔记 渲染收集是如何触发

    在 WPF 里面,渲染可以从架构上划分为两层.上层是 WPF 框架的 OnRender 之类的函数,作用是收集应用程序渲染的命令.上层将收集到的应用程序绘制渲染的命令传给下层,下层是 WPF 的 GF ...