标签:
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=4081
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5428 Accepted Submission(s): 1902
给你平面上若干的点,每个点有个权值$a_i$,现在用这些点构成一棵树。存在一种操作,可以使得这棵树上的某条边的长度变为0,令这条边连接的两个点分别是$i,j$,这棵树的边权和为$B$,问你$(a_i+a_j)/B$的最小值。
看网上的做法都是求次小生成树,而我却不是这样做的。
首先使用Kruskal求出最小生成树,而后枚举两个点,将这两个点之间连接那条边,这样就构成了一个换,为了保持树的形态,必须删掉这两个点的唯一路径上最长的边。
找到这条边的方法是通过倍增来求解。
令$ancestor[u][i]$为节点$u$向上走$2^i$所能到达的节点,$maxEdge[u][i]$表示节点$u$向上走$2^i$所能碰到的最长的边,那么有以下转移:
$$ancestor[u][i]=ancestor[ancestor[u][i-1]][i-1]$$
$$maxEdge[u][i]=max(maxEdge[u][i-1],maxEdge[ancestor[u][i-1]][i-1])$$
预处理出这两个数组后,就能用类似LCA的思想求最长边了。
#include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<algorithm> #include<iomanip> #define MAX_N 1234 #define MAX_D 25 using namespace std; int father[MAX_N]; int n; int popu[MAX_N]; struct edge { int to; double cost; edge(int t, double c) : to(t), cost(c) { } edge() { } }; struct road { int from, to; double cost; road(int f, int t, double c) : from(f), to(t), cost(c) { } road() { } }; bool cmp(road a,road b) { if (a.cost == b.cost) return popu[a.from] + popu[a.to] > popu[b.from] + popu[b.to]; return a.cost < b.cost; } road ro[MAX_N*MAX_N]; int tot=0; vector<edge> G[MAX_N]; double B; int depth[MAX_N]; int ancestor[MAX_N][25]; double maxEdge[MAX_N][25]; struct Point { double x, y; Point(double xx, double yy) : x(xx), y(yy) { } Point() { } double dis(Point a) { return sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y)); } }; Point po[MAX_N]; void init() { for (int i = 0; i <= n; i++) father[i] = i; for (int i = 0; i <= n; i++)G[i].clear(); tot = 0; B = 0; memset(depth, 0, sizeof(depth)); memset(ancestor, 0, sizeof(ancestor)); memset(maxEdge, 0, sizeof(maxEdge)); } int Find(int x){ if(x==father[x])return x; return father[x]=Find(father[x]); } void unionSet(int x,int y) { int u = Find(x), v = Find(y); if (u == v)return; father[u] = v; } bool Same(int x,int y){ return Find(x)==Find(y); } void Kruskal() { sort(ro + 1, ro + tot + 1, cmp); for (int i = 1; i <= tot; i++) { int u = ro[i].from, v = ro[i].to; if (Same(u, v))continue; unionSet(u, v); G[u].push_back(edge(v,ro[i].cost)); G[v].push_back(edge(u,ro[i].cost)); B += ro[i].cost; } } void dfs(int u,int p) { for (int i = 0; i < G[u].size(); i++) { int v = G[u][i].to; double c = G[u][i].cost; if (v == p)continue; depth[v] = depth[u] + 1; ancestor[v][0] = u; maxEdge[v][0] = c; dfs(v, u); } } void getAncestor() { for (int j = 1; j < MAX_D; j++) { for (int i = 1; i <= n; i++) { ancestor[i][j] = ancestor[ancestor[i][j - 1]][j - 1]; maxEdge[i][j] = max(maxEdge[i][j - 1], maxEdge[ancestor[i][j - 1]][j - 1]); } } } double LCA(int u,int v) { double res = -1; if (depth[u] < depth[v])swap(u, v); for (int i = MAX_D - 1; i >= 0; i--) { if (depth[ancestor[u][i]] >= depth[v]) { res = max(res, maxEdge[u][i]); u = ancestor[u][i]; if (depth[u] == depth[v])break; } } if (u == v)return res; for (int i = MAX_D - 1; i >= 0; i--) { if (ancestor[u][i] != ancestor[v][i]) { res = max(res, max(maxEdge[u][i], maxEdge[v][i])); u = ancestor[u][i]; v = ancestor[v][i]; } } return max(res, max(maxEdge[u][0], maxEdge[v][0])); } int T; int main() { cin.sync_with_stdio(false); cin >> T; while (T--) { cin >> n; init(); for (int i = 1; i <= n; i++) cin >> po[i].x >> po[i].y >> popu[i]; for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) ro[++tot] = road(i, j, po[i].dis(po[j])); Kruskal(); depth[1] = 1; dfs(1, 0); getAncestor(); double ans = -1; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) ans = max(ans, (popu[i] + popu[j]) / (B - LCA(i, j))); cout << setprecision(2) << fixed << ans << endl; } return 0; }
PS:第一次写手写倍增,居然一发就AC了
HDU 4081 Qin Shi Huang's National Road System 最小生成树+倍增求LCA
标签:
原文地址:http://www.cnblogs.com/HarryGuo2012/p/4809863.html