1.题目描述:点击打开链接
2.解题思路:本题利用LCA+MST解决。根据题意,我们需要高效的求解指定的u,v之间的最小瓶颈路(即最大的危险系数尽量小的路径)。根据生成树的结论,最小瓶颈路一定在最小生成树上。由于需要高效的处理很多查询。因此我们需要做预处理,然后在最小生成树上求最近公共祖先。
首先把最小生成树转化为有根树,然后设fa[i]表示结点i的父亲,cost[i]表示结点i和父亲之间的边权。L[i]表示结点i的深度(根结点深度为0)。求解完毕这些数组后,接下来令anc[i][j]表示结点i的第2^j级祖先的编号(j==0时候就是fa[i],如果第2^j祖先不存在,设为-1),maxcost[i][j]表示结点i和第2^j级祖先之间路径上的最大权值。那么不难推出下面的2个关系式
(1) anc(i,j)=anc(anc(i,j-1),j-1);
(2)maxcost(i,j)=max{maxcost(i,j-1),maxcost(anc(i,j-1),j-1)};
第一个关系式表示结点i的第2^j级祖先是它的第2^(j-1)级祖先的第2^(j-1)级祖先。第二个关系式表示结点i与第2^j级祖先之间路径上的最大权值等于结点j到第2^(j-1)级祖先的最大权值,和第2^(j-1)级祖先到第2^j级祖先的最大权值之间的较大者。不难得知,这样预处理的时间复杂度是O(NlogN)级别的。
预处理结束后,查询操作就可以非常快速的实现了,如果我们希望查询u-v之间路径上的最大权值。那么在求解LCA的时候就可以动态的更新答案,具体细节详见代码。查询的时间复杂度为O(logN)。
3.代码:
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<list>
#include<complex>
#include<functional>
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
#define pb push_back
typedef long long ll;
typedef pair <int,int> P;
const int N=100000+10;
const int logmaxn=20;
const int INF=1e9;
struct LCA
{
int n;
int fa[N];
int cost[N];
int L[N];
int anc[N][logmaxn];
int maxcost[N][logmaxn];
void init()
{
for(int i=0;i<n;i++)
{
anc[i][0]=fa[i];
maxcost[i][0]=cost[i];
for(int j=1;(1<<j)<n;j++)anc[i][j]=-1;
}
for(int j=1;(1<<j)<n;j++)
for(int i=0;i<n;i++)
if(anc[i][j-1]!=-1)
{
int a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
maxcost[i][j]=max(maxcost[i][j-1],maxcost[a][j-1]);
}
}
int query(int p,int q)
{
int tmp,log,i;
if(L[p]<L[q])swap(p,q);
for(log=1;(1<<log)<=L[p];log++);
log--;
int ans=-INF;
for(int i=log;i>=0;i--)
if(L[p]-(1<<i)>=L[q]) //先让p和q处于同一层
{
ans=max(ans,maxcost[p][i]);
p=anc[p][i];
}
if(p==q)return ans; //如果发现处于同一层后,恰好是同一个结点,那么LCA就是p
for(int i=log;i>=0;i--)
if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i])//否则,让p,q同时往上爬,保证爬的时候始终处于同一层
{
ans=max(ans,maxcost[p][i]);p=anc[p][i];
ans=max(ans,maxcost[q][i]);q=anc[q][i];
}
ans=max(ans,cost[p]);
ans=max(ans,cost[q]);
return ans; //LCA为fa[p](或fa[q])
}
};
LCA solver;
int pa[N];
int findset(int x){return pa[x]==x?x:pa[x]=findset(pa[x]);}
vector<int>g[N],C[N];
void dfs(int u,int fa,int level)//把MST转化为有根树
{
solver.L[u]=level;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v!=fa)
{
solver.fa[v]=u;
solver.cost[v]=C[u][i];
dfs(v,u,level+1);
}
}
}
struct Edge
{
int x,y,d;
bool operator<(const Edge&rhs)const
{
return d<rhs.d;
}
};
const int maxm=100000;
Edge e[maxm];
int main()
{
int kase=0;
int n,m,x,y,d,q;
while(~scanf("%d%d",&n,&m)&&n)
{
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&x,&y,&d);
e[i]=Edge{x-1,y-1,d};
}
sort(e,e+m);
for(int i=0;i<n;i++){pa[i]=i;g[i].clear();C[i].clear();}
for(int i=0;i<m;i++)
{
int x=e[i].x,y=e[i].y;
int d=e[i].d;
int u=findset(x),v=findset(y);
if(u!=v)
{
pa[u]=v;
g[x].push_back(y);C[x].push_back(d);
g[y].push_back(x);C[y].push_back(d);
}
}
solver.n=n;
dfs(0,-1,0);
solver.init();
if(kase++)puts("");
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&x,&y);
printf("%d\n",solver.query(x-1,y-1));
}
}
}