原理讲解:http://dongxicheng.org/structure/lca-rmq/
在线算法模板:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片 #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int INF=0x3f3f3f; const int maxn=111111; const int maxm=111111; int n,m; struct EDGENODE{ int to; int w; int next; }; struct SGRAPH{ int head[maxn]; EDGENODE edges[maxm]; int edge; void init(){ memset(head,-1,sizeof(head)); edge=0; } void addedge(int u,int v,int c){ edges[edge].w=c,edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++; } //------------ int d[maxn][20]; //元素从1编号到n void makeRmqIndex(int A[],int n){ for(int i=1;i<=n;i++) d[i][0]=i; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) d[i][j] = A[d[i][j-1]] < A[d[i+(1<<(j-1))][j-1]]? d[i][j-1]:d[i+(1<<(j-1))][j-1]; } int rmqIndex(int L,int R,int A[]) { int k=0; while ((1<<(k+1))<=R-L+1) k++; return A[d[L][k]]<A[d[R-(1<<k)+1][k]]? d[L][k]:d[R-(1<<k)+1][k]; } //--------------------- int E[maxn*2],R[maxn],D[maxn*2],mn; void dfs(int u,int p,int d){ E[++mn]=u; D[mn]=d; R[u]=mn; for (int i=head[u];i!=-1;i=edges[i].next){ int v=edges[i].to; if (v==p) continue; dfs(v,u,d+1); E[++mn]=u; D[mn]=d; } } void LCA_init(){ mn=0; memset(R,0,sizeof(R)); dfs(1,-1,1); makeRmqIndex(D,mn); getd(1,-1,0); } int LCA(int u,int v){ if (R[u]>=R[v]) return E[rmqIndex(R[v],R[u],D)]; else return E[rmqIndex(R[u],R[v],D)]; } //-------------------- int deep[maxn]; void getd(int u,int p,int w){ deep[u]=w; for (int i=head[u];i!=-1;i=edges[i].next){ int v=edges[i].to; if (v==p) continue; getd(v,u,w+edges[i].w); } } int getDis(int u,int v){ int lca=LCA(u,v); return deep[u]+deep[v]-deep[lca]*2; } int done(int x,int y,int z){ int ans=INF,res=0; int lca1,lca2; lca1=LCA(x,y); res=deep[x]+deep[y]-deep[lca1]*2; lca2=LCA(lca1,z); res+=deep[lca1]+deep[z]-deep[lca2]*2; ans=min(ans,res); lca1=LCA(x,z); res=deep[x]+deep[z]-deep[lca1]*2; lca2=LCA(lca1,y); res+=deep[lca1]+deep[y]-deep[lca2]*2; ans=min(ans,res); lca1=LCA(y,z); res=deep[y]+deep[z]-deep[lca1]*2; lca2=LCA(lca1,x); res+=deep[lca1]+deep[x]-deep[lca2]*2; ans=min(ans,res); return ans; } }solver;
/** LCA(离线算法) 主函数除建边外还应调用 init(); dir[1]=0; tarjan(1); */ #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; const int maxn=40010; struct note { int u,v,w,lca,next; }edge[maxn*2],edge1[805]; int head[maxn],ip,head1[maxn],ip1;///需要建两次边。1,该树的边2,需要查询的两点 int m,n; int father[maxn],vis[maxn],dir[maxn]; ///依次表示u点的祖先、标记是否访问过,到根节点的距离 void init() { memset(vis,0,sizeof(vis)); memset(dir,0,sizeof(dir)); memset(head,-1,sizeof(head)); memset(head1,-1,sizeof(head1)); ip=ip1=0; } void addedge(int u,int v,int w) { edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++; } void addedge1(int u,int v) { edge1[ip1].u=u,edge1[ip1].v=v,edge1[ip1].lca=-1,edge1[ip1].next=head1[u],head1[u]=ip1++; } int Find(int x) { if(father[x]==x) return x; return father[x]=Find(father[x]); } void Union(int x,int y) { x=Find(x); y=Find(y); if(x!=y) father[y]=x; } void tarjan(int u) { vis[u]=1; father[u]=u; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; int w=edge[i].w; if(!vis[v]) { dir[v]=dir[u]+w; tarjan(v); Union(u,v); } } for(int i=head1[u];i!=-1;i=edge1[i].next) { int v=edge1[i].v; if(vis[v]) { edge1[i].lca=edge1[i^1].lca=father[Find(v)]; } } }