社区讨论

树剖找不同,为什么两份代码一模一样却一个快一个慢?

P3379【模板】最近公共祖先(LCA)参与者 4已保存回复 7

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
7 条
当前快照
1 份
快照标识符
@mi7pfdjn
此快照首次捕获于
2025/11/21 01:27
4 个月前
此快照最后确认于
2025/11/21 01:27
4 个月前
查看原帖
题解:
CPP
#include<cstdio>
#include<iostream>
using namespace std;
struct edge{
    int to,ne;
}e[1000005];
int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005];
void add(int x,int y)    //加边
{
    e[++ecnt].to=y;
    e[ecnt].ne=head[x];
    head[x]=ecnt;
}
void dfs1(int x)                             //构造树
{
    siz[x]=1;                                //假设当前节点仅有一个儿子
    dep[x]=dep[f[x]]+1;                      //当前节点深度=父亲节点深度+1
    for(int i=head[x];i;i=e[i].ne)          //遍历所有的子节点
    {
        int dd=e[i].to;
        if(dd==f[x])continue;               //如果是父节点,则略过
        f[dd]=x;                             //那么确定x是当前节点的父亲
        dfs1(dd);                            //向下遍历
        siz[x]+=siz[dd];                     //遍历完子树之后,加上子树的大小
        if(!son[x]||siz[son[x]]<siz[dd])    //如果x节点重儿子未确定或者重儿子的子树比当前遍历节点的子树小
            son[x]=dd;                       //更新重儿子
    }
}

void dfs2(int x,int tv)                     //求重链
{
    top[x]=tv;                               //设置x所在重链顶为tv
    if(son[x])dfs2(son[x],tv);              //如果x有重儿子,那么随着这条重链走
    for(int i=head[x];i;i=e[i].ne)            
    {
        int dd=e[i].to; 
        if(dd==f[x]||dd==son[x])continue;  //如果走到父亲或者走到重儿子(已经走过重儿子,避免重复),那么跳过
        dfs2(dd,dd);                        //开启一条新链,链顶是其本身
    }
}
int lca(int x,int y)
{
	while(top[x]!=top[y])                              //如果二者不在同一条重链上
    { 
		if(dep[top[x]] >= dep[top[y]]) x=f[top[x]];   //选择所在重链的顶的深度较大的点向上跳,目的是防止跳过LCA
		else y=f[top[y]];
	}
	return dep[x] < dep[y] ?x :y;                     //当二者在同一条重链上的时候,选择深度较浅的点即为lca
	
	
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs1(s);
    dfs2(s,s);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
}

跑1000多ms 我的:
CPP
#include<cstdio>
#include<iostream>
using namespace std;
struct edge{
    int to,ne;
}e[1000005];
int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005];
void add(int x,int y)    //加边
{
    e[++ecnt].to=y;
    e[ecnt].ne=head[x];
    head[x]=ecnt;
}
void dfs1(int x)                             //构造树
{
    siz[x]=1;                                //假设当前节点仅有一个儿子
    dep[x]=dep[f[x]]+1;                      //当前节点深度=父亲节点深度+1
    for(int i=head[x];i;i=e[i].ne)          //遍历所有的子节点
    {
        int dd=e[i].to;
        if(dd==f[x])continue;               //如果是父节点,则略过
        f[dd]=x;                             //那么确定x是当前节点的父亲
        dfs1(dd);                            //向下遍历
        siz[x]+=siz[dd];                     //遍历完子树之后,加上子树的大小
        if(!son[x]||siz[son[x]]<siz[dd])    //如果x节点重儿子未确定或者重儿子的子树比当前遍历节点的子树小
            son[x]=dd;                       //更新重儿子
    }
}

void dfs2(int x,int tv)                     //求重链
{
    top[x]=tv;                               //设置x所在重链顶为tv
    if(son[x])dfs2(son[x],tv);              //如果x有重儿子,那么随着这条重链走
    for(int i=head[x];i;i=e[i].ne)            
    {
        int dd=e[i].to; 
        if(dd==f[x]||dd==son[x])continue;  //如果走到父亲或者走到重儿子(已经走过重儿子,避免重复),那么跳过
        dfs2(dd,dd);                        //开启一条新链,链顶是其本身
    }
}
int lca(int x,int y)
{
	while(top[x]!=top[y])                              //如果二者不在同一条重链上
    { 
		if(dep[top[x]] >= dep[top[y]]) x=f[top[x]];   //选择所在重链的顶的深度较大的点向上跳,目的是防止跳过LCA
		else y=f[top[y]];
	}
	return dep[x] < dep[y] ?x :y;                     //当二者在同一条重链上的时候,选择深度较浅的点即为lca
	
	
}
int main()
{
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs1(s);
    dfs2(s,s);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
}

跑了3000多ms
绝望

回复

7 条回复,欢迎继续交流。

正在加载回复...