pog在与szh玩游戏,首先pog在纸上画了一棵有根树,这里我们定义1为这棵树的根,然后szh在这棵树中选了若干个点,想让pog帮忙找找这些点的最近公共祖先在哪里,一个点为S的最近公共祖先当且仅当以该点为根的子树包含S中的所有点,且该点深度最大。然而,这个问题是十分困难的,出于szh对pog的爱,他决定只找编号连续的点,即 l i ~ r i 。
若干组数据(不超过 3 组 n≥10000 或 Q≥10000 )。 每组数据第一行一个整数 n(1≤n≤300000) ,表示树的节点个数。 接下来 n−1 行,每行两个数 A i ,B i ,表示存在一条边连接这两个节点。 接下来一行一个数 Q(1≤Q≤300000) ,表示有 Q 组询问。 接下来Q行每行两个数 l i ,r i (1≤li≤ri≤n) ,表示询问编号为 l i ~ r i 的点的最近公共祖先。
对于每组的每个询问,输出一行,表示编号为li~ri的点的最近公共祖先的编号。
5 1 2 1 3 3 4 4 5 5 1 2 2 3 3 4 3 5 1 5
1 1 3 3 1
思路:
做这题的方法有很多。下面给出2种解法。
1:维护一个跳表,表示编号为 i ~ i+2 j −1 的LCA,注意在这里求LCA必须用 O(1) 的做法才能通过所有数据。可以转换为RMQ,每次查询时只需查询两个数的LCA即可。
2:考虑dfs序,通过在简单的证明可知L~R的LCA为 L ~ R 中dfs序较小的那个位置与dfs序较大的那个位置的LCA。因此只要通过st表处理L~R最大dfs序与最小dfs序的编号即可。
方法一:
#include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<algorithm> using namespace std; #pragma comment(linker, "/STACK:1024000000,1024000000") const int N = 300000+1000; int Q,n; int head[N]; struct Edge { int v,nxt; }es[N<<1]; int cnt; inline void add_edge(int u,int v) { es[cnt].v=v; es[cnt].nxt=head[u]; head[u]=cnt++; es[cnt].v=u; es[cnt].nxt=head[v]; head[v]=cnt++; } int index; int vs[N*2],id[N],dep[N]; int lca[N*2][20]; int minn[N][20]; int maxn[N][20]; void dfs(int u,int fa,int h) { id[u]=++index; vs[index]=u; dep[u]=h; for(int i=head[u];~i;i=es[i].nxt) { int v=es[i].v; if(v==fa)continue; dfs(v,u,h+1); vs[++index]=u; } } int mm[2*N+100]; void ini() { memset(head,-1,sizeof(head)); cnt=index=0; } int main() { mm[0]=-1; for(int i=1;i<=2*N;i++) mm[i]= (((i-1)&i)==0)? mm[i-1]+1:mm[i-1]; while(~scanf("%d",&n)) { ini(); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } dfs(1,1,0); for(int i=1;i<=index;i++) lca[i][0]=vs[i]; for(int j=1;j<=20;j++) for(int i=1;i+(1<<j)-1<=index;i++) { int a=lca[i][j-1],b=lca[i+(1<<(j-1))][j-1]; lca[i][j] = dep[a]<dep[b] ? a:b; } for(int i=1;i<=n;i++) minn[i][0]=maxn[i][0]=id[i]; for(int j=1;j<=20;j++) for(int i=1;i+(1<<j)-1<=n;i++) { minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]); maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]); } scanf("%d",&Q); for(int i=1;i<=Q;i++) { int l,r; scanf("%d%d",&l,&r); int k=mm[r-l+1]; int L=min(minn[l][k],minn[r-(1<<k)+1][k]); int R=max(maxn[l][k],maxn[r-(1<<k)+1][k]); k=mm[R-L+1]; int a=lca[L][k],b=lca[R-(1<<k)+1][k]; int ans = dep[a]<dep[b]? a:b; printf("%d\n",ans); } } return 0; }
方法二:(防止爆栈就换成bfs)
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int N = 300000+1000; const int DEG = 20; int Q,n; int pa[N][20]; int dep[N]; int head[N]; struct Edge { int v,nxt; }es[N<<1]; int cnt; inline void add_edge(int u,int v) { es[cnt].v=v; es[cnt].nxt=head[u]; head[u]=cnt++; es[cnt].v=u; es[cnt].nxt=head[v]; head[v]=cnt++; } /* void bfs(int root) { queue<int>q; dep[root]=0; pa[root][0]=root; q.push(root); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=1;i<DEG;i++) pa[u][i]=pa[pa[u][i-1]][i-1]; for(int i=head[u];~i;i=es[i].nxt) { int v=es[i].v; if(v==pa[u][0]) continue; pa[v][0]=u; dep[v]=dep[u]+1; q.push(v); } } } */ void dfs(int u,int fa,int h) { dep[u]=h; pa[u][0]=fa; for(int i=1;i<DEG;i++) pa[u][i]=pa[pa[u][i-1]][i-1]; for(int i=head[u];~i;i=es[i].nxt) { int v=es[i].v; if(v!=fa) dfs(v,u,h+1); } } int dp[N][20]; int LCA(int u,int v) { if(dep[u]>dep[v]) swap(u,v); for(int det=dep[v]-dep[u],i=0;det;det>>=1,i++) if(det&1) v=pa[v][i]; if(u==v) return u; for(int i=DEG-1;i>=0;i--) if(pa[u][i]!=pa[v][i]) v=pa[v][i],u=pa[u][i]; return pa[u][0]; } int mm[N]; void ini() { memset(head,-1,sizeof(head)); cnt=0; } int main() { mm[0]=-1; for(int i=1;i<=N-1;i++)mm[i]= (((i-1)&i)==0)? mm[i-1]+1:mm[i-1]; while(~scanf("%d",&n)) { ini(); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } dfs(1,1,0); for(int i=1;i<=n;i++) dp[i][0]=i; for(int j=1;j<=20;j++) for(int i=1;i+(1<<j)-1<=n;i++) dp[i][j]=LCA(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); scanf("%d",&Q); for(int i=1;i<=Q;i++) { int l,r; scanf("%d%d",&l,&r); int k=mm[r-l+1]; int ans=LCA(dp[l][k],dp[r-(1<<k)+1][k]); printf("%d\n",ans); } } return 0; }