最小乘积生成树 bzoj 2395 Timeismoney

Posted foreverpiano

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小乘积生成树 bzoj 2395 Timeismoney相关的知识,希望对你有一定的参考价值。

http://www.lydsy.com/JudgeOnline/problem.php?id=2395

考虑在二维坐标系上
一种生成树方案\((cost, time)\)对应一个点
答案一定是在下凸包上的一段
我们考虑如何找到这个下凸包
首先找到两个初始点\(a, b\)
分别对应以\(cost\), \(time\)为最优
然后我们可以在\(a~b\)之间进行二分
找到离\(ab\)最远的点\(c\)
最小化\(\vec{ab} \times \vec{ac}\)也就是面积
然后对于每一条边 重新赋值
值为对答案的贡献 然后就枚举了所有下凸包的点
复杂度是\(O(S n \log n)\)
\(S\)是点对数 知乎上说是\(m^2\)级别的

#include<bits/stdc++.h>
#define int long long
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cout << #x << " = " << x << "\n"
#define type(x) __typeof((x).begin())
#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); ++ i)
using namespace std;
// by piano
template<typename tp> inline void read(tp &x) {
  x = 0;char c = getchar(); bool f = 0;
  for(; c < '0' || c > '9'; f |= (c == '-'), c = getchar());
  for(; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar());
  if(f) x = -x;
}
template<typename tp> inline void arr(tp *a, int n) {
  for(int i = 1; i <= n; i ++)
    cout << a[i] << " ";
  puts("");
}
const int N = 233;
const int M = 11233;
struct E {
  int u, v, x, y, w;
  inline void in(void) {
    read(u); read(v); read(x); read(y);
    ++ u; ++ v;
  }
}e[M];

struct Node {
  int x, y;
  Node(int _x = 0, int _y = 0) {
    x = _x; y = _y;
  }
  inline void ovo(void) {
    cout << x << " " << y << "\n";
  }
}ans, minx, miny;
int n, m, pre[N];

inline Node operator - (Node a, Node b) {
  return Node(a.x - b.x, a.y - b.y);
}

inline int crs(Node a, Node b) {
  return a.x * b.y - a.y * b.x;
}

inline int find(int u) {
  return pre[u] == u ? u : pre[u] = find(pre[u]);
}

inline bool cmp_w(E a, E b) {
  return a.w < b.w;
}

inline Node doit(void) {
  for(int i = 1; i <= n; i ++)
    pre[i] = i;
  sort(e + 1, e + m + 1, cmp_w);
  Node now;
  for(int i = 1; i <= m; i ++) {
    int fx = find(e[i].u);
    int fy = find(e[i].v);
    if(fx != fy) {
      pre[fx] = fy;
      now.x += e[i].x;
      now.y += e[i].y;
    }
  }
  if(!ans.x || (ans.x * ans.y > now.x * now.y))
    ans = now;
  return now;
}

inline void work(Node a, Node b) {
  for(int i = 1; i <= m; i ++)
    e[i].w = e[i].y * (b.x - a.x) - e[i].x * (b.y - a.y);
  Node c = doit();
  if(crs(c - a, b - a) <= 0)
    return ;
  work(a, c); work(c, b);
}

main(void) {
  read(n); read(m);
  for(int i = 1; i <= m; i ++)
    e[i].in();
  for(int i = 1; i <= m; i ++)
    e[i].w = e[i].x;
  minx = doit();
  for(int i = 1; i <= m; i ++)
    e[i].w = e[i].y;
  miny = doit();
  work(minx, miny);
  cout << ans.x << " " << ans.y << "\n";
}

以上是关于最小乘积生成树 bzoj 2395 Timeismoney的主要内容,如果未能解决你的问题,请参考以下文章

bzoj2395 [Balkan 2011]Timeismoney

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

二维最小乘积生成树学习小记

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

[BZOJ3572][HNOI2014]世界树(虚树DP)

蓝桥杯——算法提高 最小方差生成树