社区讨论
树剖找不同,为什么两份代码一模一样却一个快一个慢?
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 条回复,欢迎继续交流。
正在加载回复...