UVAlive5713 Qin Shi Huang's National Road System【次小生成树】【DP】

LINK1

LINK2


题目大意

给你平面上的n个点

每个点有一个权值

让你求出一个生成树

可以选择一条边不花费代价

要最大化这条边两边端点的权值/剩下n-2条边的长度之和

思路

发现发现其实端点权值其实不太好处理

那么我们就用最暴力的方式来枚举这样的一条边

但是显然剩下的部分不能直接暴力最小生成树

那么就直接在原图的最小生成树上面进行考虑

加上一条边一定需要删除一条边

这样的操作一定会删除两点对应生成树链上的最大边权

所以可以预处理生成树上两点之间的最大边权

这个可以用一个简单的树形dp来完成

算法复杂度\(n^2\)也没问题

也可以用主席树来标记永久化维护

但是查询的时候会多一个log

//Author: dream_maker
#include<bits/stdc++.h>
using namespace std;
//----------------------------------------------
typedef pair<int, int> pi;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define fu(a, b, c) for (int a = b; a <= c; ++a)
#define fd(a, b, c) for (int a = b; a >= c; --a)
#define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
const int INF_of_int = 1e9;
const ll INF_of_ll = 1e18;
template <typename T>
void Read(T &x) {
  bool w = 1;x = 0;
  char c = getchar();
  while (!isdigit(c) && c != '-') c = getchar();
  if (c == '-') w = 0, c = getchar();
  while (isdigit(c)) {
    x = (x<<1) + (x<<3) + c -'0';
    c = getchar();
  }
  if (!w) x = -x;
}
template <typename T>
void Write(T x) {
  if (x < 0) {
    putchar('-');
    x = -x;
  }
  if (x > 9) Write(x / 10);
  putchar(x % 10 + '0');
}
//----------------------------------------------
const int M = 1e6 + 10;
const int N = 1e3 + 10;
db dis[N][N], f[N][N];
pi pos[N];
int n, m, p[N];

db getpow(int x) {
  return (db)x * (db)x;
}

db getdis(pi x, pi y) {
  return sqrt(getpow(x.fi - y.fi) + getpow(x.se - y.se));
}

void getdis() {
  fu(i, 1, n) {
    fu(j, 1, n) {
      dis[i][j] = getdis(pos[i], pos[j]);
    }
  }
}

struct Edge {
  int u, v, nxt;
  bool operator < (const Edge b) const {
    return dis[u][v] < dis[b.u][b.v];
  }
};
struct Map {
  Edge E[M << 1];
  int head[N], tot, n;
  
  void init(int pn) {
    n = pn;
    fu(i, 1, n) head[i] = 0;
    tot = 0;
  }

  void addedge(int u, int v) {
    E[++tot] = (Edge) {u, v, head[u]};
    head[u] = tot;
  }
  
  void sortedge() {
    sort(E + 1, E + tot + 1);
  }
} mp, mst;

struct Union_Find {
  int fa[N];
  
  void init(int n) {
    fu(i, 1, n) fa[i] = i;
  }
  
  int Find(int x) {
    return x == fa[x] ? x : fa[x] = Find(fa[x]);
  }
  
  bool Merge(int x, int y) {
    int fax = Find(x), fay = Find(y);
    if (fax == fay) return 0;
    fa[fax] = fay;
    return 1;
  }
} uf;

db Kruskal() {
  uf.init(n);
  mst.init(n);
  int cnt = 0;
  db res = 0;
  fu(i, 1, mp.tot) {
    int u = mp.E[i].u, v = mp.E[i].v;
    if (uf.Merge(u, v)) {
      mst.addedge(u, v);
      mst.addedge(v, u);
      res += dis[u][v];
      if (++cnt == n - 1) break;
    }
  }
  return res;
}

bool mark[N];

void dfs(int u, int fa) {
  mark[fa] = 1;
  if (fa) {
    fu(j, 1, n) if (u != j) {
      f[u][j] = f[j][u] = max(f[fa][j], dis[u][fa]);    
    }
  }
  for (int i = mst.head[u]; i; i = mst.E[i].nxt) {
    int v = mst.E[i].v;
    if (v != fa) dfs(v, u);
  }
}

void solve() {
  Read(n);
  fu(i, 1, n)
    Read(pos[i].fi), Read(pos[i].se), Read(p[i]);
  getdis();
  mp.init(n);
  fu(i, 1, n)
    fu(j, 1, i - 1)
      mp.addedge(i, j);
  mp.sortedge();
  db tmp = Kruskal();
  fu(i, 1, n) mark[i] = 0;
  dfs(1, 0);
  db ans = 0;
  fu(i, 1, n)
    fu(j, 1, i - 1)
      ans = max(ans, db(p[i] + p[j]) / (tmp - f[i][j]));
  printf("%.2lf\n", ans);
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  int T; Read(T);
  while (T--) solve();
  return 0;
}
posted @ 2018-11-04 13:36  Dream_maker_yk  阅读(119)  评论(0编辑  收藏  举报