jzoj3555 【GDKOI2014模拟】树的直径 lca+离线

24 篇文章 0 订阅
17 篇文章 0 订阅

Description


科学家在观测一棵大树,这棵树在不断地生长,科学家给这棵树的每个节点编了号。开始的时候,这棵树很小只有4个节点,一号点为根,其他三个节点挂在上面。

在接下来的M次观察中,科学家每次都能看见这棵树从叶子处长出新的两个节点来。如果当前这棵树有N个节点,那么这棵树的新的两个节点的编号分别为N+1,N+2。科学家记录下了这棵树生长的过程,需要你帮着计算这棵树实时的直径。树的直径就是这棵树最远的两个节点的距离。

Input


第一行一个整数M,代表观察的次数。

接下来M行,每行一个整数x,代表这棵树的编号为x的节点下面又长了两个叶子节点。保证每次生长的节点都是叶子节点。

Output


M行,每次生长后这棵树的直径。

Data Constraint


对于10%的数据,N<=10

对于40%的数据,N<=1000

对于100%的数据,N<=100000。

Solution


一个显然的特点就是对于原来的最长链两端点(t1,t2),新的最长链至少有一点是其中之一。即对于新增点x,我们比较(t1,x),(x,t2),(t1,t2)的长度取最长

有一种做法就是把整棵树离线然后做lca,这样求树上路径就是nlogn的了

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define N 200005
int fa[N][19],dep[N],rec[N],q[N];
int n=4,m;
void read(int &x) {
    x=0; char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
}
int get_lca(int x,int y) {
    if (dep[x]<=dep[y]) x^=y,y^=x,x^=y;
    drp(i,18,0) if (dep[fa[x][i]]>=dep[y]) {
        x=fa[x][i];
    }
    if (x==y) return x;
    drp(i,18,0) if (fa[x][i]!=fa[y][i]) {
        x=fa[x][i];
        y=fa[y][i];
    }
    return fa[x][0];
}
int get_dis(int x,int y) {
    return dep[x]+dep[y]-dep[get_lca(x,y)]*2;
}
void add(int x) {
    fa[++n][0]=x; dep[n]=dep[x]+1;
    rep(i,1,18) fa[n][i]=fa[fa[n][i-1]][i-1];
}
int main(void) {
    read(m);
    fa[2][0]=fa[3][0]=fa[4][0]=1;
    dep[1]=0; dep[2]=dep[3]=dep[4]=1;
    rep(i,1,m) {
        read(q[i]);
        rec[i]=n+1;
        add(q[i]); add(q[i]);
    }
    int t1=2,t2=3;
    int dis1=get_dis(t1,t2);
    rep(i,1,m) {
        int dis2=get_dis(t1,rec[i]);
        int dis3=get_dis(t2,rec[i]);
        if (dis2>=dis1&&dis2>=dis3) {
            t2=rec[i];
            printf("%d\n",dis1=dis2);
        } else if (dis3>=dis1&&dis3>=dis2) {
            t1=rec[i];
            printf("%d\n",dis1=dis3);
        } else {
            printf("%d\n",dis1);
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值