首先说这道题的思路:存在多条路径时求路径最大权的最小值问题,可以利用生成树的性质重建给出的图,将多路径转化为一定最优的单一路径,再在树上求LCA时统计权值来求出“路径最大权的最小值”。
写这份代码的时候复习了几个重要的知识点:
并查集
在写生成树时,经常会用到并查集。
ll find(ll x)
{
if(b[x]==x) return x;
else return b[x]=find(b[x]);
}
并查集的代码非常简单,需要注意的是,在合并时,语句为:
f[find(x)]=find(y);
而不是:
f[x]=find(y);
下面的写法会漏合并x所在的集合的其他点 (我在这里卡了是真的丢人)
LCA
这道题中的LCA与一般的LCA大同小异,只是在求 2 i 2^i 2i级父节点时一起将到 2 i 2^i 2i级父节点的“路径最大权的最小值”一起求出来,在倍增求LCA时也加入统计“路径最大权的最小值”的代码即可。同时,由于这道题可能给出森林,因此需要将每个点都作为根来dfs,同时加入特判来判定某个点是否已经属于某棵已知的树。
void dfs(ll k,ll fa)
{
if(v[k]) return;//特判
v[k]=1;
d[k]=d[fa]+1;
f[k][0]=fa;
for(int i=1;(1<<i)<d[k];i++)
{
f[k][i]=f[f[k][i-1]][i-1];
w[k][i]=min(w[k][i-1],w[f[k][i-1]][i-1]);//求“路径最大权的最小值”,与求f[k][i]同理
}
for(int i=head[k];i;i=l[i].n)
{
if(l[i].r==fa) continue;
w[l[i].r][0]=l[i].w;
dfs(l[i].r,k);
}
}
ll lca(ll u,ll p)
{
ll ans=maxn;
if(find(u)!=find(p)) return -1;
if(d[u]<d[p]) swap(u,p);
for(int i=19;i>=0;i--)
if(f[u][i]&&d[f[u][i]]>=d[p])
{
ans=min(ans,w[u][i]);//统计“路径最大权的最小值”
u=f[u][i];
}
if(u==p) return ans;
for(int i=19;i>=0;i--)
{
if(f[u][i]!=f[p][i])
{
ans=min(ans,min(w[u][i],w[p][i]));//统计“路径最大权的最小值”
u=f[u][i];
p=f[p][i];
}
}
ans=min(ans,min(w[u][0],w[p][0]));//统计“路径最大权的最小值”
return ans;
}
其他重要操作还有前向星和生成树,不过这些较为简单,就不再赘述,代码见下方即可。
AC代码:
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+5;
ll n,m,x,y,z,q;
ll b[maxn];//并查集
ll head[maxn],ecnt=1;//前向星
ll v[maxn],f[maxn][20],d[maxn];//lca
ll w[maxn][20];//lca中到父节点的最小边权
struct edge{
int l,r,w;
}e[maxn];
struct list{
int r,n,w;
}l[maxn];
bool cmp(edge a,edge b) {return a.w>b.w;}
ll find(ll x)
{
if(b[x]==x) return x;
else return b[x]=find(b[x]);
}
void swap(int &a,int &b)
{
int c=a;
a=b;
b=c;
}
void add(ll a,ll b,ll c)
{
l[ecnt].r=b;
l[ecnt].w=c;
l[ecnt].n=head[a];
head[a]=ecnt++;
}
void scs()
{
for(int i=1;i<=n;i++) b[i]=i;
for(int i=1;i<=m;i++)
{
if(find(e[i].l)!=find(e[i].r))
{
b[find(e[i].l)]=find(e[i].r);
add(e[i].l,e[i].r,e[i].w);
add(e[i].r,e[i].l,e[i].w);
}
}
}
void dfs(ll k,ll fa)
{
if(v[k]) return;
v[k]=1;
d[k]=d[fa]+1;
f[k][0]=fa;
for(int i=1;(1<<i)<d[k];i++)
{
f[k][i]=f[f[k][i-1]][i-1];
w[k][i]=min(w[k][i-1],w[f[k][i-1]][i-1]);
}
for(int i=head[k];i;i=l[i].n)
{
if(l[i].r==fa) continue;
w[l[i].r][0]=l[i].w;
dfs(l[i].r,k);
}
}
ll lca(ll u,ll p)
{
ll ans=maxn;
if(find(u)!=find(p)) return -1;
if(d[u]<d[p]) swap(u,p);
for(int i=19;i>=0;i--)
if(f[u][i]&&d[f[u][i]]>=d[p])
{
ans=min(ans,w[u][i]);
u=f[u][i];
}
if(u==p) return ans;
for(int i=19;i>=0;i--)
{
if(f[u][i]!=f[p][i])
{
ans=min(ans,min(w[u][i],w[p][i]));
u=f[u][i];
p=f[p][i];
}
}
ans=min(ans,min(w[u][0],w[p][0]));
return ans;
}
/*void out()
{
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=l[j].n)
printf("%d %d\n",l[j].r,l[j].w);
}
}*/
int main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].w);
sort(e+1,e+m+1,cmp);
/* printf("\n");
for(int i=1;i<=m;i++) printf("%d %d %d\n",e[i].l,e[i].r,e[i].w);*/
scs();
/* printf("\n");
out();
printf("\n");*/
for(int i=1;i<=n;i++) dfs(i,0);
scanf("%lld",&q);
for(int i=1;i<=q;i++)
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",lca(x,y));
}
return 0;
}
以及,善用注释!