codeforces 379 F New Year Tree(树的直径)

You are a programmer and you have a New Year Tree (not the traditional fur tree, though) — a tree of four vertices: one vertex of degree three (has number 1), connected with three leaves (their numbers are from 2 to 4).

On the New Year, programmers usually have fun. You decided to have fun as well by adding vertices to the tree. One adding operation looks as follows:

First we choose some leaf of the tree with number v.
Let’s mark the number of vertices on the tree at this moment by variable n, then two vertexes are added to the tree, their numbers are n + 1 and n + 2, also you get new edges, one between vertices v and n + 1 and one between vertices v and n + 2.
Your task is not just to model the process of adding vertices to the tree, but after each adding operation print the diameter of the current tree. Come on, let’s solve the New Year problem!

Input
The first line contains integer q (1 ≤ q ≤ 5·105) — the number of operations. Each of the next q lines contains integer vi (1 ≤ vi ≤ n) — the operation of adding leaves to vertex vi. Variable n represents the number of vertices in the current tree.

It is guaranteed that all given operations are correct.

Output
Print q integers — the diameter of the current tree after each operation.

Examples
Input
5
2
3
4
8
5
Output
3
4
4
5
6

题意:

原始有一棵根为1,有三个叶子2,3,4的树。有t个操作,每次可以在一个叶子下面续上两个节点,每次操作完问当前树的直径。

思路:

  • 第一个想到的是用树型DP。因为求直径有一个树型DP的做法,对于每个点记录以他为最浅节点的最长路,最后对于每一个节点的DP值取max。在这题里,每操作一次,都需要更新一条链上所有的点。一开始我以为这是O(nlgn)的,然而只要他一直在新增节点下面加节点,这棵树的深度就会非常大(实际上就是一条链了),于是这种方法退化为O(n^2),对于5e5的数据是绝对过不去的

  • 所以现在目标很明确了,就是找到一个真正的O(nlgn)算法,这个n是树的深度。于是开始往倍增上面凑 (假的)

  • 可以知道求树的直径有另一种方法,即跑两边DFS。对于这种方法的证明中,有一条树的性质,即从树上任意一点能走到的最远的点一定是直径上的点。由这一条性质可知,新增的节点到树上一点最远的路径一定是他的父亲的最远路径加上他和父亲的连边,所以每次新增一个节点,只要分别求他到原直径两端的距离,若大于原直径长度则更新直径。用倍增求距离O(lgn),绝对不会炸了。

  • 最后还有一个问题就是直径可能有很多条,如果只记一条,如何保证更新节点时不会有他到其他直径端点的距离更优。实际上这只需要感性认识,因为所有直径都必定交于一段连续的区间,所以在更新一条直径时其实也是更新了其他直径。

  • 最后上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e6+10;
int q, s, t, n, maxl;
int f[N][22], dpt[N];

void INIT()
{
    memset(f, 0, sizeof(f));
    dpt[1] = 0; dpt[2] = dpt[3] = dpt[4] = 1;
    f[2][0] = f[3][0] = f[4][0] = 1;
    n = 4; s = 2; t = 4;
    maxl = 2;
}

void UPDATE(int u, int fa)
{
    f[u][0] = fa;
    dpt[u] = dpt[fa]+1;
    for (int i = 1; i <= 21; i++)
        f[u][i] = f[f[u][i-1]][i-1];
}

int LEN(int x, int y)
{
    // cout << x << " " << y << endl;
    if (x == y) return 0;
    if (dpt[x] < dpt[y]) swap(x, y);
    int len = 0;
    for (int i = 21; i >= 0; i--)
        if (dpt[x]-(1<<i) >= dpt[y])
            len += (1<<i), x = f[x][i];
    for (int i = 21; i >= 0; i--)
        if (f[x][i] != f[y][i])
            len += 2*(1<<i), x = f[x][i], y = f[y][i];
    // cout << len << "!" << endl;
    if (x != y) len += 2;
    return len;
}

int main()
{
    INIT();
    scanf("%d", &q);
    while (q--){
        int x;
        scanf("%d", &x);
        UPDATE(n+1, x);
        UPDATE(n+2, x);
        int l = LEN(n+1, s);
        if (l > maxl){
            t = n+1;
            maxl = l;
        }
        l = LEN(n+1, t);
        if (l > maxl){
            s = n+1;
            maxl = l;
        }
        printf("%d\n", maxl);
        n += 2;
        // cout << s << " " << t << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/81365637