[洛谷P3379]【模板】最近公共祖先(LCA)
题目大意:就是叫你求最近公共祖先。
最近刚学了倍增,于是用这道模板题来练练。
有人说倍增写法要读入优化,要卡常,然而经过我的测试,发现并不需要(尽管我加了读优,比原来的要快400+ms)。
C++ Code:
#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
int n,m,s;
int head[1000005],next[1000005],to[1000005],cnt=0,deep[500005],p[500005][20];
void addedge(int x,int y){
cnt++;
to[cnt]=y;
next[cnt]=head[x];
head[x]=cnt;
cnt++;
to[cnt]=x;
next[cnt]=head[y];
head[y]=cnt;
}
void dfs(int u){
for(int i=head[u];i;i=next[i])
if(!deep[to[i]]){
deep[to[i]]=deep[u]+1;
p[to[i]][0]=u;
dfs(to[i]);
}
}
void init(){
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
if(p[i][j-1]!=-1)p[i][j]=p[p[i][j-1]][j-1];
}
int lca(int x,int y){
int i;
if(deep[x]<deep[y])x^=y,y^=x,x^=y;
for(i=0;(1<<i)<=deep[x];i++);i--;
for(int j=i;j>=0;j--)
if(deep[p[x][j]]>=deep[y])x=p[x][j];
if(x==y)return x;
for(int j=i;j>=0;j--)
if(p[x][j]!=-1&&p[x][j]!=p[y][j])x=p[x][j],y=p[y][j];
return p[x][0];
}
#define C c=getchar()
int readint(){
int C;
while(!isdigit(c))C;
int p=0;
while(isdigit(c)){
p=p*10+c-'0';
C;
}
return p;
}
int main(){
n=readint(),m=readint(),s=readint();
memset(p,-1,sizeof(p));
for(int i=1;i<n;i++)addedge(readint(),readint());
deep[s]=1;
dfs(s);
init();
while(m--)printf("%d\n",lca(readint(),readint()));
return 0;
}
另外附上以前写的Tarjan,比倍增快了1000+ms(尽管我也不相信倍增比Tarjan慢,然而事实就是这样)。
C++ Code:
#include<cstdio>
#include<cstring>
#include<vector>
#include<cctype>
using namespace std;
int n,m,ne=0,nq=0;
bool vis[500001],instack[500001];
int f[500001],head[500001],que[500001],ans[500001];
struct query{
int same,next,to,num;
bool flag;
}q[1000001];
struct edge{
int to,next;
}e[1000001];
void add_edge(int x,int y){
e[++ne].to=y;
e[ne].next=head[x];
head[x]=ne;
e[++ne].to=x;
e[ne].next=head[y];
head[y]=ne;
}
void add_que(int x,int y,int z){
q[++nq].to=y;
q[nq].same=nq+1;
q[nq].num=z;
q[nq].next=que[x];
que[x]=nq;
q[++nq].to=x;
q[nq].same=nq-1;
q[nq].num=z;
q[nq].next=que[y];
que[y]=nq;
}
int find(int x){
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
void tarjan(int root){
instack[root]=true;
for(int i=head[root];i;i=e[i].next){
int v=e[i].to;
if(instack[v])continue;
tarjan(v);
f[v]=root;
vis[v]=true;
}
for(int i=que[root];i;i=q[i].next)
if(vis[q[i].to]&&!q[i].flag){
ans[q[i].num]=find(q[i].to);
q[i].flag=q[q[i].same].flag=true;
}
instack[root]=false;
}
#define C c=getchar()
int readint(){
char C;
while(!isdigit(c))C;
int d=0;
while(isdigit(c)){
d=d*10+c-'0';
C;
}
return d;
}
int main(){
int t;
n=readint(),m=readint(),t=readint();
memset(vis,0,sizeof(vis));
memset(instack,0,sizeof instack);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<n;i++){
int u=readint(),v=readint();
add_edge(u,v);
}
for(int i=1;i<=m;i++){
int x=readint(),y=readint();
add_que(x,y,i);
}
tarjan(t);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
----------------------------------------------------------2017.7.13----------------------------------------------------------
最近学了树链剖分,于是用树剖写了个LCA,不过速度居然没有Tarjan快。
C++ Code:
#include<cstdio>
#include<cctype>
#include<algorithm>
using std::swap;
#define C c=getchar()
int n,m,s;
int head[500005],next[1000005],cnt=0,to[1000005],deep[500005],fa[500005],sz[500005],top[500005];
inline int readint(){
char C;
while(!isdigit(c))C;
int p=0;
while(isdigit(c))p=p*10+c-'0',C;
return p;
}
inline void addedge(int u,int v){
to[++cnt]=v;
next[cnt]=head[u];
head[u]=cnt;
to[++cnt]=u;
next[cnt]=head[v];
head[v]=cnt;
}
void dfs(int x){
deep[x]=deep[fa[x]]+1;
sz[x]=1;
for(int i=head[x];i;i=next[i])
if(fa[x]!=to[i]){
fa[to[i]]=x;
dfs(to[i]);
sz[x]+=sz[to[i]];
}
}
void dfs2(int x){
if(!top[x])top[x]=x;
int t=0;
for(int i=head[x];i;i=next[i])
if(fa[x]!=to[i]&&sz[to[i]]>sz[t])t=to[i];
if(t)top[t]=top[x],dfs2(t);
for(int i=head[x];i;i=next[i])
if(fa[x]!=to[i]&&to[i]!=t)dfs2(to[i]);
}
int query(int x,int y){
for(;top[x]!=top[y];x=fa[top[x]])
if(deep[top[x]]<deep[top[y]])swap(x,y);
return deep[x]<deep[y]?x:y;
}
int main(){
n=readint(),m=readint(),s=readint();
for(int i=1;i<n;++i)addedge(readint(),readint());
dfs(s);dfs2(s);
while(m--)printf("%d\n",query(readint(),readint()));
return 0;
}

浙公网安备 33010602011771号