zoukankan      html  css  js  c++  java
  • [洛谷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
    ",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
    ",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
    ",query(readint(),readint()));
    	return 0;
    }
    
  • 相关阅读:
    7-2 一元多项式的乘法与加法运算 (20 分)
    cvc-complex-type.2.4.a: Invalid content was found starting with element(servlet)
    MOOC 2.3 队列
    MOOC 2.2 堆栈
    MOOC 2.1 线性表及其实现
    MOOC 1.3 最大子列和
    计时程序
    MOOC 1.1 什么是数据结构
    poj3253
    二分法查找——对数
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/7153725.html
Copyright © 2011-2022 走看看