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

来源:互联网 发布:ppt播放软件下载 编辑:程序博客网 时间:2024/06/08 10:55

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 200005int 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;}
原创粉丝点击