LCA解决瓶颈问题
以前一直以为要特意怎么样去弄,后来才知道只要在LCA倍增的过程中顺带着往上求出最小值就可以了,毕竟一段距离总是可以被拿二的几次方表示出来的,所以一定可以覆盖住每一段的最小值
时间有限,先放代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int head[500000];
int head2[500000];
int q;
int n,m;
struct Edge
{
int from;
int to;
int next;
int w;
friend bool operator <(Edge a,Edge b)
{
return a.w<b.w;
}
} edge[500000],edge2[50000];
int cnt=1;
void add(int u,int v,int len)
{
edge[cnt].from=u;
edge[cnt].to=v;
edge[cnt].w=len;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void add2(int u,int v,int len)
{
edge2[cnt].from=u;
edge2[cnt].to=v;
edge2[cnt].w=len;
edge2[cnt].next=head2[u];
head2[u]=cnt++;
}
int pre[500000];
int find(int x)
{
if(pre[x]==x)
return x;
else
{
pre[x]=find(pre[x]);
return pre[x];//直接把它的父亲节点归到祖宗节点上
}
}
void Union(int x,int y)
{
//如果两者不同属于同一集合
pre[find(x)]=find(y);//把x的祖宗归到y的祖宗身上
}
bool cmp(Edge a,Edge b)
{
return a.w>b.w;
}
int d[500000][40];//d[i][j]表示i点上翻2的j次方个点(也就是有2的j次方个边)的最小值!
int f[500000][40];
int deep[500000];//方便计算LCA的一个东西!
int fa[500000];
bool vis[500000];
void dfs(int father,int u,int w)
{
vis[u]=true;
fa[u]=father;
f[u][0]=father;
d[u][0]=w; //记住,在树中,我们都上翻而不是下翻!
deep[u]=deep[father]+1;
for(int i=head2[u]; i!=-1; i=edge2[i].next)
{
int vi=edge2[i].to;
int w1=edge2[i].w;
if(vi==father)
continue;
if(!vis[vi])
dfs(u,vi,w1);
}
return;
}
void lca_init()
{
for(int j=1; j<=30; j++)
for(int i=1; i<=n; i++)
{
f[i][j]=f[f[i][j-1]][j-1];
}
for(int j=1; j<=30; j++)
for(int i=1; i<=n; i++)
{
d[i][j]=min(d[i][j-1],d[f[i][j-1]][j-1]);//集成父亲
}//算的不就是这俩吗?费事!
return;
}
int lca(int x,int y)
{
if(deep[x]<deep[y])swap(x,y);//令x深度最大
int mi=0x7fffffff;
for(int j=30; j>=0; j--)
{
if(deep[f[x][j]]>=deep[y])
{
mi=min(mi,d[x][j]);//每次二进制上翻时顺带算上就好
x=f[x][j];
}
if(x==y)return mi;//向上翻但x仍然大于等于y
}
for(int j=30; j>=0; j--)
{
if(f[x][j]!=f[y][j])
{
mi=min(mi,d[x][j]);
mi=min(mi,d[y][j]);//先把上回的处理完
x=f[x][j];y=f[y][j];
}
}
mi=min(mi,d[x][0]);
mi=min(mi,d[y][0]);
return mi;
}
int main()
{
memset(head,-1,sizeof(head));
memset(head2,-1,sizeof(head2));
scanf("%d%d",&n,&m);
int x,y,z;
int q;
for(int i=1; i<=n; i++)
pre[i]=i;
for(int i=1; i<=m; i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
sort(edge+1,edge+1+m,cmp);//对各边重新排序
cnt=1;
for(int i=1; i<=m; i++)
{
x=edge[i].from;
y=edge[i].to;
z=edge[i].w;
int ab=find(x);
int cd=find(y);
if(ab!=cd)
{
Union(x,y);
add2(x,y,z);
add2(y,x,z);//重新建边
}
}
for(int i=0; i<=n; i++)
{
for(int j=0; j<=20; j++)
{
d[i][j]=0x7fffffff;
}
}
dfs(0,1,0);
scanf("%d",&q);
lca_init();
for(int i=1; i<=q; i++)
{
scanf("%d%d",&x,&y);
if(find(x)!=find(y))
{
printf("-1\n");
continue;
}
int ans=lca(x,y);
printf("%d\n",ans);
}
}