[关闭]
@Metralix 2018-03-08T14:41:45.000000Z 字数 4349 阅读 775

寒假训练 DIV1(5)生成树


A - Qin Shi Huang's National Road System

最短路


题目大意

有n个城市,要修一些路使得任意两个城市都能连通。但是有人答应可以不计成本的帮你修一条路,为了使成本最低,你要慎重选择修哪一条路。假设其余的道路长度为B,那条别人帮忙修的道路两端城市中的总人口为B,要找一个使A/B最大的方案。

解题思路

 先求最小生成树,处理出MST中任意两点之间的最长边。因为别人只答应给修一条路,所以枚举这条路,用这条路替换下一条MST中最长的边(在加入这条路后构成的环中),比较求得A/B的最大值。实际上是把求次小生成树的一些后续操作改改。
  1. #include<stack>
  2. #include<queue>
  3. #include<cmath>
  4. #include<cstdio>
  5. #include<cstring>
  6. #include<iostream>
  7. #include<algorithm>
  8. #pragma commment(linker,"/STACK: 102400000 102400000")
  9. using namespace std;
  10. const double eps=1e-6;
  11. const int INF=0x3f3f3f3f;
  12. const int MAXN=1e3+20;
  13. struct city
  14. {
  15. int x,y,p;
  16. };
  17. city c[MAXN];
  18. int V,par[MAXN];//par数组记录生成树上i的上一个点
  19. bool used[MAXN][MAXN],visit[MAXN];
  20. double mincost[MAXN],cost[MAXN][MAXN],dp[MAXN][MAXN];
  21. double prim()
  22. {
  23. memset(visit,false,sizeof(visit));
  24. memset(dp,0,sizeof(dp));
  25. memset(used,false,sizeof(used));
  26. for(int i=1; i<=V; i++)
  27. {
  28. mincost[i]=cost[1][i];
  29. par[i]=1;
  30. }
  31. visit[1]=true;
  32. double res=0;
  33. for(int i=1; i<V; i++)
  34. {
  35. int v=-1;
  36. for(int j=1; j<=V; j++)
  37. if(!visit[j]&&(v==-1||mincost[j]<mincost[v]))
  38. v=j;
  39. if(v==-1)
  40. break;
  41. visit[v]=true;
  42. used[v][par[v]]=used[par[v]][v]=true;
  43. res+=mincost[v];
  44. for(int j=1; j<=V; j++)
  45. {
  46. if(!visit[j]&&cost[v][j]<mincost[j])
  47. {
  48. mincost[j]=cost[v][j];
  49. par[j]=v;
  50. }
  51. if(visit[j]&&j!=v)
  52. {
  53. dp[v][j]=dp[j][v]=max(dp[j][par[v]],mincost[v]);
  54. }
  55. }
  56. }
  57. return res;
  58. }
  59. int main()
  60. {
  61. #ifndef ONLINE_JUDGE
  62. freopen("in.txt","r",stdin);
  63. #endif // ONLINE_JUDGE
  64. int tcase;
  65. scanf("%d",&tcase);
  66. while(tcase--)
  67. {
  68. scanf("%d",&V);
  69. for(int i=1; i<=V; i++)
  70. scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].p);
  71. for(int i=1; i<=V; i++)
  72. for(int j=1; j<=V; j++)
  73. cost[i][j]=cost[j][i]=sqrt((c[i].x-c[j].x)*(c[i].x-c[j].x)+(c[i].y-c[j].y)*(c[i].y-c[j].y));
  74. double mst=prim();
  75. double ans=-1;
  76. for(int i=1; i<=V; i++)
  77. {
  78. for(int j=1; j<=V; j++)
  79. {
  80. if(i==j)
  81. continue;
  82. if(used[i][j])//i和j之间的边是否在最小生成树上
  83. ans=max(ans,(c[i].p+c[j].p+0.0)/(mst-cost[i][j]));
  84. else
  85. ans=max(ans,(c[i].p+c[j].p+0.0)/(mst-dp[i][j]));
  86. }
  87. }
  88. printf("%.2lf\n",ans);
  89. }
  90. return 0;
  91. }

B - Bond

标签: 并查集


题目大意

  给出一张n个点m条边的无向图, 每条边有一个危险度,有q个询问, 每次给出两个点s、t,找一条路, 使得路径上的最大危险度最小。

解题思路

首先,我们可以发现,如果求一个最小生成树, 那么任意两点, 在生成树上有唯一路径, 而且这条路径上的最大危险值一定最小。 但是n和q都太大, 如果直接顺着树走,每次询问最大复杂度O(n), 那么复杂度高达O(n^2),会超时。   我们知道, 并查集在用了路径压缩之后效率高达O(n), 但是却破坏了树形结构, 所以不能用路径压缩。  然而我们经常忽视了按秩合并这个方法, 即使不用路径压缩, 仅仅靠按秩合并, 复杂度也可低至O(logn)。  因此我们只需按秩合并, 然后询问的时候向根回溯就行了, 复杂度mlogn。
  1. #include <iostream>
  2. #include <cstdio>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <queue>
  6. using namespace std;
  7. const int maxn=55000;
  8. const int maxm=250000;
  9. const int INF=0x3f3f3f3f;
  10. typedef long long LL;
  11. struct HeapNode{
  12. int d,u;
  13. HeapNode(){}
  14. HeapNode(int a,int b):d(a),u(b){}
  15. bool operator<(const HeapNode& rhs) const{
  16. return d>rhs.d;
  17. }
  18. };
  19. struct EdgeNode{
  20. int to;
  21. int w;
  22. int next;
  23. };
  24. struct Prim{
  25. EdgeNode edges[maxm];
  26. int head[maxn];
  27. int edge,n;
  28. void init(int n){
  29. this->n=n;
  30. memset(head,-1,sizeof(head));
  31. edge=0;
  32. }
  33. void addedge(int u,int v,int c){
  34. edges[edge].w=c,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;
  35. }
  36. bool done[maxn];
  37. int dis[maxn];
  38. int pre[maxn];
  39. int dep[maxn];
  40. void prim(int s){
  41. priority_queue<HeapNode>que;
  42. for (int i=0;i<=n;i++) dis[i]=INF;
  43. dis[s]=0;
  44. memset(done,0,sizeof(done));
  45. memset(dep,0,sizeof(dep));
  46. que.push(HeapNode(0,s));
  47. while (!que.empty()){
  48. HeapNode x=que.top();
  49. que.pop();
  50. int u=x.u;
  51. if (done[u]) continue;
  52. done[u]=true;
  53. for (int i=head[u];i!=-1;i=edges[i].next){
  54. int v=edges[i].to;
  55. int w=edges[i].w;
  56. if (!done[v]&&dis[v]>w){
  57. dis[v]=w;
  58. pre[v]=u;
  59. dep[v]=dep[u]+1;
  60. que.push(HeapNode(dis[v],v));
  61. }
  62. }
  63. }
  64. }
  65. }solver;
  66. int n,m;
  67. int fa[maxn],cost[maxn],L[maxn];
  68. int anc[maxn][20];
  69. int maxCost[maxn][20];
  70. void preprocess(){
  71. for (int i=1;i<=n;i++){
  72. anc[i][0]=fa[i];
  73. maxCost[i][0]=cost[i];
  74. for (int j=1;(1<<j)<n;j++) anc[i][j]=-1;
  75. }
  76. for (int j=1;(1<<j)<n;j++){
  77. for (int i=1;i<=n;i++){
  78. if (anc[i][j-1]!=-1){
  79. int a=anc[i][j-1];
  80. anc[i][j]=anc[a][j-1];
  81. maxCost[i][j]=max(maxCost[i][j-1],maxCost[a][j-1]);
  82. }
  83. }
  84. }
  85. }
  86. int query(int p,int q){
  87. int log;
  88. if (L[p]<L[q]) swap(p,q);
  89. for (log=1;(1<<log)<=L[p];log++);log--;
  90. int ans=-INF;
  91. for (int i=log;i>=0;i--){
  92. if (L[p]-(1<<i)>=L[q]){
  93. ans=max(ans,maxCost[p][i]);
  94. p=anc[p][i];
  95. }
  96. }
  97. if (p==q) return ans;
  98. for (int i=log;i>=0;i--){
  99. if (anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){
  100. ans=max(ans,maxCost[p][i]);
  101. p=anc[p][i];
  102. ans=max(ans,maxCost[q][i]);
  103. q=anc[q][i];
  104. }
  105. }
  106. ans=max(ans,cost[p]);
  107. ans=max(ans,cost[q]);
  108. return ans;
  109. }
  110. int main()
  111. {
  112. int cas=0;
  113. while (~scanf("%d%d",&n,&m)){
  114. if (cas!=0) puts("");
  115. cas++;
  116. solver.init(n);
  117. for (int i=0;i<m;i++){
  118. int x,y,z;
  119. scanf("%d%d%d",&x,&y,&z);
  120. solver.addedge(x,y,z);
  121. solver.addedge(y,x,z);
  122. }
  123. solver.prim(1);
  124. for (int i=1;i<=n;i++){
  125. fa[i]=solver.pre[i];
  126. cost[i]=solver.dis[i];
  127. L[i]=solver.dep[i];
  128. }
  129. preprocess();
  130. int Q;
  131. scanf("%d",&Q);
  132. while (Q--){
  133. int x,y;
  134. scanf("%d%d",&x,&y);
  135. printf("%d\n",query(x,y));
  136. }
  137. }
  138. return 0;
  139. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注