【做题笔记】P3884[JLOI2009]二叉树问题

这题不难......我是用倍增 LCA 实现的,很简单。不会看这里:https://www.cnblogs.com/BlueInRed/p/12644150.html

注意输出的顺序......是深度 宽度 距离......

注意这里距离的定义......


以上是我这题做了一个晚上之后得出的惨痛教训。

来看看最大深度怎么求。

倍增 LCA 不是在预处理 \(f\) 数组的时候就能顺便求出每个点的深度了吗?

然后每一次比较一下深度更新答案即可。

这里一定要注意:最大深度不一定是第 \(n\) 个点。

预处理+求深度 \(d\) 数组代码

void dfs(int u, int fa)
{
    d[u] = d[fa] + 1;
    deep = max(deep, d[u]);
    
    for(int i=1; (1<<i) <= d[u]; i++)
        f[u][i] = f[ f[u][i-1] ][i-1];

    for(int i=head[u]; i; i=nxt[i])
    {
        int y=ver[i];
        if(y == fa) continue;
        f[y][0] = u;
        dfs(y, u);
    }
}

来看宽度怎么求。

这个比较麻烦。

如果直接统计,我们无法得知一个点到底在哪一层,从而无从得知每一层的宽度。

那么注意到同一层不同点的深度是一样的,所以直接把深度当成层的编号,用深度更新每一层的宽度即可。

更新之后比较一下即可。

代码:

for(int i=1; i<=n; i++) wid[ d[i] ] += 1; //这个结点所在的深度的宽度+1
int width=0;
for(int i=1; i<=n; i++) width = max(width, wid[ d[i] ]); //找出宽度最大的那一层

来看距离怎么求。

首先求出 \(\text{LCA}(u,v)\)

题目中说:

注:结点间距离的定义:由结点向根方向(上行方向)时的边数×2,与由根向叶结点方向(下行方向)时的边数之和。

上行方向的边数就是(假设 \(\text{LCA}(u,v)=lca\) ):

\[d_u-d_{lca} \]

实际上就是前缀和的思想:减去要求的区间之外的部分。

×2就是:

\[(d_u-d_{lca})\times 2 \]

下行方向的边数更好求了,就是:

\[(d_v-d_{lca}) \]

综上,\(u,v\) 的距离就是:

\[(d_u-d_{lca})\times 2+(d_v-d_{lca}) \]

故我们可以得出最终代码:

#include <cstdio>
#include <algorithm>
using namespace std;

int tot, n, ver[100010], nxt[100010], head[100010];
int f[100010][30], u, v, d[100010], wid[100010], deep;

void add(int x, int y)
{
    ver[++tot] = y; nxt[tot] = head[x]; head[x] = tot;
}

void dfs(int u, int fa)
{
    d[u] = d[fa] + 1;
    deep = max(deep, d[u]);
    
    for(int i=1; (1<<i) <= d[u]; i++)
        f[u][i] = f[ f[u][i-1] ][i-1];

    for(int i=head[u]; i; i=nxt[i])
    {
        int y=ver[i];
        if(y == fa) continue;
        f[y][0] = u;
        dfs(y, u);
    }
}

int LCA(int a, int b)
{
    if(d[a] < d[b]) swap(a, b);

    for(int i=20; i>=0; i--)
    {
        if(d[ f[a][i] ] >= d[b]) a = f[a][i];
        if(a == b) return a;
    }

    for(int i=20; i>=0; i--)
        if(f[a][i] != f[b][i])
            a = f[a][i], b = f[b][i];

    return f[a][0]; // return f[b][0];
}

int main(void)
{
    scanf("%d", &n);
    for(int i=1; i<=(n-1); i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y);
        add(y, x);
    }

    dfs(1, 0);
    
    scanf("%d%d", &u, &v);
    // printf("%d\n", (d[u]+d[v] - 2 * LCA(u, v)) * 2 );

    int lca = LCA(u, v);

    for(int i=1; i<=n; i++) wid[ d[i] ] += 1;
    int width=0;
    for(int i=1; i<=n; i++) width = max(width, wid[ d[i] ]);

    printf("%d\n%d\n%d\n", deep, width, (d[u] - d[lca])*2 + d[v]-d[lca] );
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/BlueInRed/p/12644165.html
今日推荐