LCA的五种解法
标准求法
//O(nlogn)-O(logn) #include<cstdio> #include<algorithm> using namespace std; const int maxn=100010; int first[maxn],next[maxn*2],to[maxn*2],dis[maxn*2]; int n,m; void AddEdge(int a,int b,int c) { to[++m]=b; dis[m]=c; next[m]=first[a]; first[a]=m; } int dep[maxn],fa[maxn],cost[maxn],anc[20][maxn]; void dfs(int x) { dep[x]=dep[fa[x]]+1; for(int i=first[x];i;i=next[i]) { if(to[i]!=fa[x]) { fa[to[i]]=x; cost[to[i]]=cost[x]+dis[i]; dfs(to[i]); } } } void preprocess() { for(int i=1;i<=n;i++) anc[0][i]=fa[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i<=n;i++) if(anc[j-1][i]) { int a=anc[j-1][i]; anc[j][i]=anc[j-1][a]; } } int LCA(int p,int q) { if(dep[p]<dep[q]) swap(p,q); int log=1; for(;(1<<log)<=dep[p];log++); log--; for(int i=log;i>=0;i--) if(dep[p]-dep[q]>=(1<<i)) p=anc[i][p]; if(p==q) return p; for(int i=log;i>=0;i--) if(anc[i][p]!=anc[i][q]) { p=anc[i][p]; q=anc[i][q]; } return fa[p]; } int main() { int a,b,c,Q; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); AddEdge(a,b,c); AddEdge(b,a,c); } scanf("%d",&Q); dfs(1); preprocess(); while(Q--) { scanf("%d%d",&a,&b); c=LCA(a,b); printf("%d %d\n",dep[a]+dep[b]-dep[c]*2+1,cost[a]+cost[b]-cost[c]*2); } return 0; }
欧拉序列套区间最小值
//O(nlogn)-O(1) #include<iostream> #include<cstdio> using namespace std; const int maxn=100010; int n,e=1; int first[maxn],next[maxn*2],dis[maxn*2],to[maxn*2]; void AddEdge(int a,int b,int c) { to[e]=b; dis[e]=c; next[e]=first[a]; first[a]=e++; } int fa[maxn],dep[maxn],d[maxn],p[maxn],A[maxn*2],z=1; void dfs(int x) { dep[x]=dep[fa[x]]+1; p[x]=z; A[z++]=x; for(int i=first[x];i;i=next[i]) { if(fa[x]!=to[i]) { d[to[i]]=d[x]+dis[i]; fa[to[i]]=x; dfs(to[i]); A[z++]=x; } } } int mv[20][maxn*2],len[maxn*2]; void RMQ_init(int m) { for(int i=1;i<=m;i++) mv[0][i]=A[i]; for(int j=1;(1<<j)<=m;j++) for(int i=1;i+(1<<j)<=m+1;i++) { mv[j][i]=mv[j-1][i]; if(dep[mv[j-1][i]]>dep[mv[j-1][i+(1<<(j-1))]]) mv[j][i]=mv[j-1][i+(1<<(j-1))]; } for(int i=1;i<=m;i++) while(1<<(len[i]+1)<=i) len[i]++; } int LCA(int a,int b) { if(a<b) swap(a,b); int k=len[a-b+1]; return dep[mv[k][b]]<dep[mv[k][a-(1<<k)+1]]?mv[k][b]:mv[k][a-(1<<k)+1]; } int main() { int a,b,c,Q; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); AddEdge(a,b,c); AddEdge(b,a,c); } dfs(1); RMQ_init(z-1); scanf("%d",&Q); while(Q--) { scanf("%d%d",&a,&b); c=LCA(p[a],p[b]); printf("%d %d\n",dep[a]+dep[b]-2*dep[c]+1,d[a]+d[b]-2*d[c]); } return 0; }
树链剖分
//O(n)-O(logn) #include<cstdio> #include<algorithm> using namespace std; const int maxn=100010; struct Edge { int b,c; }es[maxn*2]; int n,m=1; int first[maxn],next[maxn*2]; void AddEdge(int a,int b,int c) { es[m]=(Edge){b,c}; next[m]=first[a]; first[a]=m++; } int dep[maxn],siz[maxn],son[maxn],fa[maxn],dis[maxn]; void dfs(int x) { siz[x]=1; dep[x]=dep[fa[x]]+1; for(int i=first[x];i;i=next[i]) { Edge& e=es[i]; if(e.b!=fa[x]) { fa[e.b]=x; dis[e.b]=dis[x]+e.c; dfs(e.b); siz[x]+=siz[e.b]; if(siz[son[x]]<siz[e.b]) son[x]=e.b; } } } int top[maxn]; void build(int x) { if(son[x]) { top[son[x]]=top[x]; build(son[x]); } for(int i=first[x];i;i=next[i]) { Edge& e=es[i]; if(e.b!=fa[x]&&e.b!=son[x]) { top[e.b]=e.b; build(e.b); } } } int query(int va,int vb) { int f1=top[va],f2=top[vb]; while(f1!=f2) { if(dep[f1]<dep[f2]) { swap(f1,f2); swap(va,vb); } va=fa[f1]; f1=top[va]; } if(dep[va]>dep[vb]) swap(va,vb); return va; } int main() { int a,b,c,Q; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); AddEdge(a,b,c); AddEdge(b,a,c); } dfs(1); top[1]=1; build(1); scanf("%d",&Q); while(Q--) { scanf("%d%d",&a,&b); c=query(a,b); printf("%d %d\n",dep[a]+dep[b]-2*dep[c]+1,dis[a]+dis[b]-2*dis[c]); } return 0; }
Tarjan算法(离线)
//O(n)-O(1) #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; int n,m; int first[maxn],next[maxn*2],to[maxn*2],d[maxn*2]; void AddEdge(int a,int b,int c) { to[++m]=b; d[m]=c; next[m]=first[a]; first[a]=m; } int f2[maxn],n2[maxn*2],v[maxn*2],id[maxn*2]; void AddQuery(int a,int b,int c) { v[++m]=b; id[m]=c; n2[m]=f2[a]; f2[a]=m; } int vis[maxn],dis[maxn],dep[maxn],f[maxn],ans[maxn]; int findset(int x) {return x==f[x]?x:f[x]=findset(f[x]);} void dfs(int x,int fa) { f[x]=x; dep[x]=dep[fa]+1; for(int i=first[x];i;i=next[i]) { if(to[i]!=fa) { dis[to[i]]=dis[x]+d[i]; dfs(to[i],x); f[to[i]]=f[x]; } } vis[x]=1; for(int i=f2[x];i;i=n2[i]) if(vis[v[i]]) ans[id[i]]=findset(v[i]); } int A[maxn],B[maxn]; int main() { scanf("%d",&n); int a,b,c,Q; for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); AddEdge(a,b,c); AddEdge(b,a,c); } scanf("%d",&Q); m=0; for(int i=0;i<Q;i++) { scanf("%d%d",&A[i],&B[i]); AddQuery(A[i],B[i],i); AddQuery(B[i],A[i],i); } dfs(1,0); for(int i=0;i<Q;i++) printf("%d %d\n",dep[A[i]]+dep[B[i]]-dep[ans[i]]*2+1,dis[A[i]]+dis[B[i]]-dis[ans[i]]*2); return 0; }
LCT
//O(n)-O(logn) #include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int maxn=100010; int n,m; int first[maxn],next[maxn*2],to[maxn*2],d[maxn*2]; void AddEdge(int a,int b,int c) { to[++m]=b; d[m]=c; next[m]=first[a]; first[a]=m; } int ans1,ans2; struct LCT { int pre[maxn],ch[maxn][2],fa[maxn],v[maxn],sumv[maxn],s[maxn]; void init() { memset(pre,0,sizeof(pre)); memset(ch,0,sizeof(ch)); s[0]=fa[1]=v[0]=sumv[0]=0; } void maintain(int x) { sumv[x]=v[x]+sumv[ch[x][0]]+sumv[ch[x][1]]; s[x]=s[ch[x][0]]+s[ch[x][1]]+1; } void rotate(int x,int d) { int y=pre[x],z=pre[y]; ch[y][d^1]=ch[x][d]; pre[ch[x][d]]=y; ch[z][ch[z][1]==y]=x; pre[x]=z; ch[x][d]=y; pre[y]=x; maintain(y); } void splay(int x) { int rt=x; while(pre[rt]) rt=pre[rt]; if(rt!=x) { fa[x]=fa[rt]; fa[rt]=0; while(pre[x]) rotate(x,ch[pre[x]][0]==x); } maintain(x); } void access(int x) { int y=0; for(;x;x=fa[x]) { splay(x); fa[ch[x][1]]=x; pre[ch[x][1]]=0; ch[x][1]=y; fa[y]=0; pre[y]=x; y=x; maintain(x); } } void query(int x,int y) { access(y); for(y=0;x;x=fa[x]) { splay(x); if(!fa[x]) { ans1=s[ch[x][1]]+s[y]+1; ans2=sumv[ch[x][1]]+sumv[y]; return; } fa[ch[x][1]]=x; pre[ch[x][1]]=0; ch[x][1]=y; fa[y]=0; pre[y]=x; y=x; maintain(x); } } }sol; int vis[maxn]; queue<int> Q; void BFS(int x) { vis[1]=1; Q.push(1); while(!Q.empty()) { x=Q.front(); Q.pop(); for(int i=first[x];i;i=next[i]) if(!vis[to[i]]) { vis[to[i]]=1; sol.fa[to[i]]=x; sol.v[to[i]]=d[i]; Q.push(to[i]); sol.maintain(to[i]); } } } int main() { scanf("%d",&n); int a,b,c,Q; for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); AddEdge(a,b,c); AddEdge(b,a,c); } sol.init(); BFS(1); scanf("%d",&Q); while(Q--) { scanf("%d%d",&a,&b); sol.query(a,b); printf("%d %d\n",ans1,ans2); } return 0; }