cf685 B. Kay and Snowflake(树形dp,树的重心)

题意:

找每棵子树的重心。(叙述与原题意有点不同,但可以按这个做)

思路:

首先dfs,求每棵子树的大小 sz[u] ,以及最大的直接儿子子树的大小 mx[u]

以 u-子树 中的某点 cen 为重心,最大的连通块大小为 get = max(sz[u]-sz[cen], mx[cen])

当重心沿着树链往上走(不超过u)时,get要么单调递增,要么先递减后递增。那么每个让 v-子树的重心往上走,尝试更新答案。

注意每个重心往上走的过程是单调的,不需要往回走。这保证了复杂度是 \(O(n)\)

const int N = 3e5 + 5;
int n, q, fa[N];

int h[N], e[N], ne[N], idx;
void add(int a, int b) {
    e[++idx] = b, ne[idx] = h[a], h[a] = idx;
}

int sz[N], mx[N]; //最大的直接儿子子树的大小
void dfs1(int u)
{
    sz[u] = 1;
    for(int i = h[u]; i; i = ne[i])
    {
        int v = e[i]; dfs1(v);
        sz[u] += sz[v];
        mx[u] = max(mx[u], sz[v]);
    }
}

int get(int u, int cen)
{
    return max(sz[u]-sz[cen], mx[cen]);
}

int ans[N];
void dfs2(int u)
{
    ans[u] = u;
    for(int i = h[u]; i; i = ne[i])
    {
        int v = e[i]; dfs2(v);
        for(int p = ans[v]; p != u; p = fa[p])
        {
            if(get(u, ans[u]) > get(u, p)) ans[u] = p;
            if(get(u, fa[p]) >= get(u, p)) break;
        }
    }
}

main()
{
    cin >> n >> q;
    for(int i = 2; i <= n; i++) cin >> fa[i], add(fa[i], i);

    dfs1(1); dfs2(1);

    while(q--)
    {
        int t; cin >> t;
        cout << ans[t] << endl;
    }
}

posted @ 2022-02-13 21:41  Bellala  阅读(60)  评论(0)    收藏  举报