题目:https://www.luogu.org/problemnew/show/P1967
倍增LCA裸题。用了在线。还有离线O(n)做法、树链剖分做法,暂不管了。
(自己程序的)坑点:1.xnt从1开始!2.数组大小!!!
重边在最大生成树的时候就解决啦~
不然我就要进了子节点的dfs以后遍历一遍它的边,从与fa相连者中找出最大的作为mn[ ][0]的值;用vis标记使fa再走到自己时不进dfs了。
值得注意的地方是它可能是一个森林。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e5+5,M=5e5+5,lm=17;const ll INF=0x7fffffff; int n,m,head[N],xnt=1,dep[N],pre[N][lm+5],q; ll mn[N][lm+5],fa[N]; bool use[M<<1],in[N]; struct Edge{ int next,from,to,bh;ll w; Edge(int n=0,int f=0,int t=0,int b=0,ll w=0):next(n),from(f),to(t),bh(b),w(w) {} }edge[M<<1],tp[N<<1]; bool cmp(Edge a,Edge b){return a.w>b.w;} int find(int a) { if(fa[a]==a)return a; return fa[a]=find(fa[a]); } void kruskal() { memcpy(tp,edge,sizeof tp); sort(tp+1,tp+xnt+1,cmp); for(int i=1,u,v,k;i<=xnt;i++) if(find(u=tp[i].from)!=find(v=tp[i].to)) fa[find(u)]=find(v),use[k=tp[i].bh]=use[k^1]=1; } void dfs(int cur,int fa) { pre[cur][0]=fa;dep[cur]=dep[fa]+1; for(int i=1;i<=lm;i++) { int k=pre[cur][i-1];//不是i>>1 if(!pre[k][i-1])break; pre[cur][i]=pre[k][i-1]; mn[cur][i]=min(mn[cur][i-1],mn[k][i-1]);//不用赋初值了 } for(int i=head[cur],v;i;i=edge[i].next) if(use[i]&&(v=edge[i].to)!=fa)mn[v][0]=edge[i].w,dfs(v,cur); } ll lca(int x,int y) { ll ans=INF; if(dep[x]<dep[y])swap(x,y); int d=dep[x]-dep[y]; for(int i=lm;i>=0;i--)// if(d&(1<<i)){ans=min(ans,mn[x][i]);x=pre[x][i];} if(x==y)return ans;// for(int i=lm;i>=0;i--) if(pre[x][i]!=pre[y][i]) {ans=min(ans,min(mn[x][i],mn[y][i]));x=pre[x][i];y=pre[y][i];} return min(ans,min(mn[x][0],mn[y][0])); } int main() { scanf("%d%d",&n,&m);int x,y;ll z; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++) { scanf("%d%d%lld",&x,&y,&z); edge[++xnt]=Edge(head[x],x,y,xnt,z);head[x]=xnt; edge[++xnt]=Edge(head[y],y,x,xnt,z);head[y]=xnt; } kruskal(); for(int i=1;i<=n;i++) if(fa[i]==i)dfs(i,0);//森林 // for(int j=1;j<=lm;j++) // for(int i=1;i<=n;i++) // { // mn[i][j]=min(mn[pre[i][j-1]][j-1],mn[i][j-1]); // pre[i][j]=pre[pre[i][j-1]][j-1]; // } scanf("%d",&q); for(int i=1;i<=q;i++) { scanf("%d%d",&x,&y); if(find(x)!=find(y))printf("-1 "); else printf("%lld ",lca(x,y)); } }