【题意】给定N<=5*10^4,M<=1*10^5的图,Q<=5*10^4个询问,(u,v)选择一条路径使得最大的边权最小。
【解题方法】找最小,显然先MST。然后用倍增搞一搞就行了,熟练剖分也可以,算是树剖入门题了,我这里用倍增来搞一下。
【AC 代码】
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 5e5+10;
const int maxm = 1e6+10;
int n,m,q;
struct edge1{
int u,v,w;
bool operator<(const edge1 &rhs) const{
return w<rhs.w;
}
};
vector<edge1>E1;
int head[maxn],tot;
struct edge{
int v,w,next;
}E[maxm];
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void addedge(int u,int v,int w)
{
E[tot].v=v,E[tot].w=w,E[tot].next=head[u],head[u]=tot++;
}
int fa[maxn];
int Find(int x)
{
if(x==fa[x]) return x;
else return fa[x]=Find(fa[x]);
}
void Kruskal()
{
for(int i=1; i<=n; i++) fa[i]=i;
sort(E1.begin(),E1.end());
init();
int num=0;
for(auto &e:E1)
{
int u=Find(e.u),v=Find(e.v);
if(u==v) continue;
fa[u]=v;
addedge(e.u,e.v,e.w);
addedge(e.v,e.u,e.w);
++num;
if(num==n-1) break;
}
}
int dep[maxn],p[20][maxn],maxv[20][maxn];
void dfs(int u,int f,int d,int w)
{
dep[u]=d;
p[0][u]=f,maxv[0][u]=w;
for(int i=head[u]; ~i; i=E[i].next)
{
int v=E[i].v;
if(v==f) continue;
dfs(v,u,d+1,E[i].w);
}
}
void build()
{
dfs(1,-1,0,0);
for(int i=0; i+1<18; i++)
{
for(int v=1; v<=n; v++)
{
if(p[i][v]<0) p[i+1][v]=-1;
else p[i+1][v]=p[i][p[i][v]];
maxv[i+1][v] = max(maxv[i][v],maxv[i][p[i][v]]);
}
}
}
int lca(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v);
for(int i=0; i<18; i++) if(dep[u]-dep[v]>>i&1) v=p[i][v];
if(u==v) return u;
for(int i=17; ~i; i--)
if(p[i][u]!=p[i][v])
u=p[i][u],v=p[i][v];
return p[0][u];
}
int get(int u,int to)
{
int ret=0;
for(int i=0; i<18; i++)
if(dep[u]-dep[to]>>i&1) ret=max(ret,maxv[i][u]),u=p[i][u];
return ret;
}
int main()
{
int cas=0;
while(scanf("%d%d",&n,&m)!=EOF)
{
E1.clear();
int u,v,w;
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&u,&v,&w);
E1.push_back(edge1{u,v,w});
}
Kruskal();
build();
scanf("%d",&q);
if(cas++) printf("\n");
while(q--)
{
scanf("%d%d",&u,&v);
int _lca=lca(u,v);
printf("%d\n",max(get(u,_lca),get(v,_lca)));
}
}
return 0;
}