UVA 11354 Bond 邦德 (RMQ,最小瓶颈MST)

题意:n个城市,m条路,每条路有个危险值,要使得从s走到t的危险值最小。回答q个询问,每个询问有s和t,要求输出从s到t最小的危险值。(5万个点,10万条边)

思路:其实要求的是任意点对之间的最小瓶颈路的权值。

  先对图求一次MST,那么所有的瓶颈路都在上面。但是q<=5万,即使预先求出所有点对,也需要O(n*n),太大了。如果对于每个询问才来找答案,这又更慢了。所以得优化。

  优化方案(1):先求出生成树,对于每次询问的两个点,求一次LCA,两个点到LCA所经过的边其中一条边就是答案。很不幸,如果树很稀疏,树枝又很长,那么每次求LCA相当于O(n)的复杂度。

  优化方案(2):鉴于方案(1),要解决的是树枝很长的情况,由于要求的是区间最值,那么类似于RMQ问题了,只是在树上而不是序列中而已。假如将某个点p到其LCA之间的一条链作为一个区间的话,那么这段区间的最值,就是p到(p+LCA)/2与(p+LCA)/2到LCA两个区间的最值再取最值。这又变成了类似于ST算法了。

  

  具体做法可以这样:

  (1)预处理每个节点p到p+1,p+2...直到树根的这段区间的最值,那么得到m[p,p+1],m[p,p+2],m[p,p+4]...。大小是以2的幂为单位的。若某个点到树根的距离并不是刚好为2的整数次幂,那么这段放弃掉,因为总有点到树根的距离是恰好满足的,那么必要时,找这个区间即可。

  (2)在查询时,两节点分别到他们的LCA所经过的边不断更新答案。

  此代码用于优化求LCA的速度,以及从p到LCA这段区间的最值。

 1 void preprocess(int n)//预处理2的幂次大小的区间内的最值
 2 {
 3     for(int i=1; i<=n; i++) //初始化,及先记录到父亲的距离
 4     {
 5         anc[i][0]=far[i];
 6         maxcost[i][0]=cost[i][far[i]];
 7         for(int j=1; (1<<j)<n; j++)
 8             anc[i][j]=-1;
 9     }
10     for(int j=1; (1<<j)<n; j++)
11     {
12         for(int i=1; i<=n; i++ )
13         {
14             int e=anc[i][j-1];
15             if(e>0)
16             {
17                 anc[i][j]=anc[e][j-1];      //将2的j次幂,分成两个2的j-1次来组成。
18                 maxcost[i][j]=max(maxcost[i][j-1],maxcost[e][j-1]);
19             }
20         }
21     }
22 }
23
24
25
26 int query(int p,int q)  //先找LCA,再求答案
27 {
28     int tmp, log, i;
29     if(level[p]<level[q])   swap(p,q);//保持p更深一些
30
31     for(log=1; (1<<log)<=level[p]; log++);//统计最深要几层
32     --log;
33     int ans=-1;
34     for(int i=log; i>=0; i--)//将p提到与q同层
35     {
36         if(level[p]-(1<<i)>=level[q])//这一步关键,只要多于level[q]的部分都会被抹去
37         {
38             ans=max(ans, maxcost[p][i]);
39             p=anc[p][i];
40         }
41     }
42
43     if(p==q)    return ans;//LCA为q
44
45     for(int i=log; i>=0; i--)//同时往LCA方向更新,直到成为LCA的孩子
46     {
47         if(anc[p][i]>0 && anc[p][i]!=anc[q][i] )
48         {
49             ans=max(ans, maxcost[p][i]);
50             p=anc[p][i];
51             ans=max(ans, maxcost[q][i]);
52             q=anc[q][i];
53         }
54     }
55     ans=max(ans, cost[p][far[p]]);//还有一层,别忘了
56     ans=max(ans, cost[q][far[q]]);
57
58     return ans;
59 }

主要优化代码注释

  

  1 #include <bits/stdc++.h>
  2 #define INF 0x7f7f7f7f
  3 #define pii pair<int,int>
  4 #define LL long long
  5 using namespace std;
  6 const int N=100010;
  7 int a[N], b[N], w[N], seq[N];     //求MST用的
  8 int pre[N], level[N], far[N], maxcost[N][100], anc[N][100];
  9 int vis[N];
 10 vector<int>  vect[N];    //建树时用
 11 map<int,int> cost[N];
 12
 13 int cmp(int a,int b){return w[a]<w[b];}
 14 int find(int x){return pre[x]==x? x: pre[x]=find(pre[x]);}  //并查集
 15
 16
 17
 18 void DFS(int x,int flo)
 19 {
 20     vis[x]=1;
 21     level[x]=flo;  //记录在哪一层
 22     for(int i=0; i<vect[x].size(); i++)
 23     {
 24         int t=vect[x][i];
 25         if(!vis[t])
 26         {
 27             far[t]=x;  //用孩子索引父亲
 28             DFS(t, flo+1);
 29         }
 30     }
 31 }
 32
 33 void preprocess(int n)
 34 {
 35     for(int i=1; i<=n; i++) //对于每个点
 36     {
 37         anc[i][0]=far[i];
 38         maxcost[i][0]=cost[i][far[i]]; //与父亲的距离。
 39         for(int j=1; (1<<j)<n; j++)    //初始化祖先为-1
 40             anc[i][j]=-1;
 41     }
 42     for(int j=1; (1<<j)<n; j++)     //仅需到n-1即可。
 43     {
 44         for(int i=1; i<=n; i++ )
 45         {
 46             int e=anc[i][j-1];
 47             if(e>0)
 48             {
 49                 anc[i][j]=anc[e][j-1];      //将2的j次幂,分成两个2的j-1次来组成。
 50                 maxcost[i][j]=max(maxcost[i][j-1],maxcost[e][j-1]);
 51             }
 52         }
 53     }
 54 }
 55
 56
 57 int cal(int n,int m)
 58 {
 59     memset(maxcost, 0, sizeof(maxcost));
 60     memset(far, 0,   sizeof(far));
 61     memset(level, 0,  sizeof(level));
 62     memset(anc, 0x8f, sizeof(anc));
 63     //kruscal
 64     for(int i=0; i<=n; i++)
 65     {
 66         pre[i]=i;
 67         cost[i].clear();
 68         vect[i].clear();
 69     }
 70     for(int i=0; i<m; i++)
 71     {
 72         int u=find( a[seq[i]] );
 73         int v=find( b[seq[i]] );
 74         if(u!=v)
 75         {
 76             pre[u]=v;
 77             vect[a[seq[i]]].push_back(b[seq[i]]);
 78             vect[b[seq[i]]].push_back(a[seq[i]]);
 79             cost[a[seq[i]]][b[seq[i]]]=w[seq[i]];   //因为目前还不知道谁是父亲
 80             cost[b[seq[i]]][a[seq[i]]]=w[seq[i]];
 81         }
 82     }
 83     //dfs
 84     memset(vis, 0, sizeof(vis));
 85     DFS(1, 0);
 86     preprocess(n);
 87     return 0;
 88 }
 89
 90
 91
 92 int query(int p,int q)  //先找LCA,再求答案
 93 {
 94     int tmp, log, i;
 95     if(level[p]<level[q])   swap(p,q);
 96
 97     for(log=1; (1<<log)<=level[p]; log++);
 98     --log;
 99     int ans=-1;
100     for(int i=log; i>=0; i--)
101     {
102         if(level[p]-(1<<i)>=level[q])
103         {
104             ans=max(ans, maxcost[p][i]);
105             p=anc[p][i];
106         }
107     }
108
109     if(p==q)    return ans;
110
111     for(int i=log; i>=0; i--)
112     {
113         if(anc[p][i]>0 && anc[p][i]!=anc[q][i] )
114         {
115             ans=max(ans, maxcost[p][i]);
116             p=anc[p][i];
117             ans=max(ans, maxcost[q][i]);
118             q=anc[q][i];
119         }
120     }
121     ans=max(ans, cost[p][far[p]]);
122     ans=max(ans, cost[q][far[q]]);
123     return ans;
124 }
125
126 int main()
127 {
128     freopen("input.txt", "r", stdin);
129     int n, m, q, x, y, k=0;
130
131     while(~scanf("%d%d",&n,&m))
132     {
133         if(k!=0)  printf("\n");//格式
134         k++;
135         for(int i=0; i<m; i++)
136         {
137             seq[i]=i;   //边号
138             scanf("%d%d%d", &a[i], &b[i], &w[i]);
139         }
140         sort(seq, seq+m, cmp);
141         cal(n, m);
142         scanf("%d",&q);
143         for(int i=0; i<q; i++)
144         {
145             scanf("%d%d",&x,&y);
146             printf("%d\n", query(x,y));
147         }
148     }
149     return 0;
150 }

AC代码

时间: 2024-08-04 01:06:35

UVA 11354 Bond 邦德 (RMQ,最小瓶颈MST)的相关文章

uva 11354 bond 最小生成树

n个城市通过m条无向边连接,回答q个询问,每个询问形式为s,t,要找到一条s到t的路使得这条路上的最大危险系数最小. 还是最小瓶颈路,可是要快速回答每次询问,先求出最小生成树,转化为有根树,即找到s到t的路径上的最大边,在这一过程中倍增查找. 预处理的复杂度为nlogn,每次查询为logn. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using na

UVa 11354 Bond 最小生成树+LCA倍增

题目来源:UVa 11354 Bond 题意:n个点m条边的图 q次询问 找到一条从s到t的一条边 使所有边的最大危险系数最小 思路:使最大的危险系数尽量小 答案是最小生成树上的边 然后用LCA倍增法记录s和t到他们最近公共祖先的最大值 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 50010; const int INF =

UVA 11354 - Bond(树链剖分)

UVA 11354 - Bond 题目链接 题意:给定一个图,要求每次询问两点,求出这两点间路径最大危险系数最小 思路:先求最小生成树,在生成树上每次询问求LCT就可以了,利用树链剖分求解 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define lson(x) ((x<<1)+1) #d

uva 11354 - Bond(树链剖分)

题目链接:uva 11354 - Bond 题目大意:给定一张图,每次询问两个节点路径上进过边的危险值的最大值的最小值. 解题思路:首先建立最小生成数,然后根据这棵树做树链剖分. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int maxn = 50005; const int INF = 0x3f

UVA 11354 Bond(最小生成树+lca+倍增求祖先节点)

题意:n个点m条边,每条边有一个权值,有q个询问,每次询问两点间的一条路径,使得这条路径上权值最大的边最小. 思路:很容易想到最小瓶颈路,但是查询太多,会超时,可以预处理出最小生成树,则问题转化为一棵树上的两点间路径中权值最大的那条边,设这两点为u,v,可以得到dist(u,v)=max(dist(u,lca(u,v)),dist(v,lca(v,lca))),其中lca(u,v)表示u和v的最近公共祖先,用倍增的思想预处理出每个结点的2^i的祖先fa[u][i],然后在离线求出lca后顺便计算

UVA 11354 Bond 瓶颈路 最小生成树+LCA类似

题目链接:点击打开链接 题意: 给定n个点m条边的无向图 下面m行是(u,v) 和边权 下面q个询问 (u, v) 在这两个点间找一条路径使得这个路径上最大的边权最小. 数据保证询问的2个点之间一定存在路径 思路: 求瓶颈路,最小生成树跑一下. 然后求lca的代码里加入边权. 因为要使得最大的边权最小,所以用最小生成树的krusal算法, 正确性证明: 我们现在有联通块G,一个孤立点u,以及G与u之间的一些边,则显然我们选择边权最小的一条边把2个联通块连接. 这个过程就是krusal. #inc

UVA - 11354 Bond(最小生成树+LCA+瓶颈路)

题意:N个点,M条路,每条路的危险度为路上各段中最大的危险度.多组询问,点s到点t的所有路径中最小的危险度. 分析: 1.首先建个最小生成树,则s到t的路径一定是危险度最小的. 原因:建最小生成树的最后一步,如果有两个相等的边可以选择,然后将两个连通块连在一起. 那不管选择哪个边,对于分别位于两个连通块的两点来说,这条边都是必经之路,而这个必经之路是这两点路径的危险度中最大的,起决定作用,所以选哪个是一样的. 2.利用lca,在找s和t最近公共祖先的过程中,不断取两者路径中的最大危险度即可. 3

UVA 11354 Bond

Bond Time Limit: 8000ms Memory Limit: 131072KB This problem will be judged on UVA. Original ID: 1135464-bit integer IO format: %lld      Java class name: Main Once again, James Bond is on his way to saving the world. Bond's latest mission requires hi

UVA 11354 - Bond (最小生成树 + 树链剖分)

题目链接~~> 做题感悟:这题开始看到时感觉不是树不好处理,一想可以用 Kruskal 处理成树 ,然后就好解决了. 解题思路: 先用 Kruskal 处理出最小生成树,然后用树链剖分 + 线段树处理就可以了. 代码: #include<iostream> #include<sstream> #include<map> #include<cmath> #include<fstream> #include<queue> #incl