【洛谷P4331】Sequence 數字序列
阿新 • • 發佈:2020-12-02
最近公共祖先
1.暴力
將點按照父子關係依次向上查詢,知道兩個點發生重合
# include <stdio.h> # include <string.h> # define N 40010 # define M 2*N using namespace std; int Next[M],head[N],ver[M],edge[M]; int depth[N],father[M],D[M]; int tot=-1,n,m; void ADD(int x,int y,int z) { ver[++tot]=y; edge[tot]=z; Next[tot]=head[x]; head[x]=tot; } int LCA(int x,int y) { while(x!=y) { if(depth[x]>=depth[y])x=father[x]; else y=father[y]; } return x; } void dfs(int x,int fa) { int y,z; father[x]=fa; depth[x]=depth[fa]+1; for(int i=head[x]; ~i; i=Next[i]) { y=ver[i]; z=edge[i]; if(y==fa)continue; D[y]=D[x]+z; dfs(y,x);//查詢兒子節點 }//此dfs是為了求得depth[]陣列 } int main() { int T,x,y,z; register int i; scanf("%d%d",&n,&m); memset(head,0xff,sizeof(head)); tot=-1; for(int i=1; i<n; i++) { scanf("%d%d%d",&x,&y,&z); ADD(x,y,z); ADD(y,x,z);//雙向邊 } dfs(1,-1); while(m--) { scanf("%d%d",&x,&y); printf("%d\n",D[x]+D[y]-2*D[LCA(x,y)]); } return 0; }
2.樹上倍增
在進行 dfs 的時候處理出每個節點的父子關係,在查詢時以 \(2^i\) 為單位向上倍增,先將兩個節點的深度調整一致,然後將兩個點同時向上查詢,最後兩點重合時,該點即為兩個點的LCA,同時也可結合倍增的處理一些權值的問題
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<math.h> #include<vector> #include<queue> #define ll long long const ll maxn=1e5+10; ll n,q,tot,ans1,ans2; ll head[maxn*2],dep[maxn]; ll fa[maxn][22],minn[maxn][22],maxx[maxn][22]; struct node { ll u,v,w,nxt; } s[maxn*2]; inline void add(ll u,ll v,ll w) { s[++tot].v=v; s[tot].w=w; s[tot].nxt=head[u]; head[u]=tot; } inline void dfs(ll x,ll f) { fa[x][0]=f; dep[x]=dep[f]+1; for(int i=head[x];i;i=s[i].nxt) { ll y=s[i].v; if(y==f) continue; minn[y][0]=s[i].w; maxx[y][0]=s[i].w; dfs(y,x); } } inline void lca(ll x,ll y) { if(dep[x]<dep[y]) std::swap(x,y); for(int i=18;i>=0;i--) { if(dep[fa[x][i]]>=dep[y]) { ans1=std::max(ans1,maxx[x][i]); ans2=std::min(ans2,minn[x][i]); x=fa[x][i]; } } if(x==y) return ; for(int i=18;i>=0;i--) { if(fa[x][i]!=fa[y][i]) { ans1=std::max(ans1,std::max(maxx[x][i],maxx[y][i])); ans2=std::min(ans2,std::min(minn[x][i],minn[y][i])); x=fa[x][i]; y=fa[y][i]; } } ans1=std::max(ans1,std::max(maxx[x][0],maxx[y][0])); ans2=std::min(ans2,std::min(minn[x][0],minn[y][0])); } int main(void) { scanf("%lld",&n); memset(minn,0x3f,sizeof(minn)); for(int i=1;i<=n-1;i++) { ll x,y,z; scanf("%lld %lld %lld",&x,&y,&z); add(x,y,z); add(y,x,z); } dfs(1,0); for(int j=1;j<=18;j++) { for(int i=1;i<=n;i++) { fa[i][j]=fa[fa[i][j-1]][j-1]; minn[i][j]=std::min(minn[i][j-1],minn[fa[i][j-1]][j-1]); maxx[i][j]=std::max(maxx[i][j-1],maxx[fa[i][j-1]][j-1]); } } scanf("%lld",&q); for(int i=1;i<=q;i++) { ll x,y; ans1=-maxn*maxn*maxn; ans2=maxn*maxn*maxn; scanf("%lld %lld",&x,&y); lca(x,y); printf("%lld %lld\n",ans2,ans1); } return 0; }