zoukankan      html  css  js  c++  java
  • 【做题笔记】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] ]); //找出宽度最大的那一层
    

    来看距离怎么求。

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

    题目中说:

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

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

    [d_u-d_{lca} ]

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

    ×2就是:

    [(d_u-d_{lca}) imes 2 ]

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

    [(d_v-d_{lca}) ]

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

    [(d_u-d_{lca}) imes 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
    ", (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
    %d
    %d
    ", deep, width, (d[u] - d[lca])*2 + d[v]-d[lca] );
        return 0;
    }
    
  • 相关阅读:
    CTO这点事(技术,业务,管理,情商,周期,趋势)转
    Unit Test相关问题汇总
    优秀的命令行文件传输程序(转)
    ZOJ3329之经典概率DP
    Sql Server 2005 开发版亲測可用下载地址
    Hadoop与HBase中遇到的问题
    Struts2自己定义拦截器实例—登陆权限验证
    手游Apk破解疯狂,爱加密apk加固保护开发人员
    HighCharts 具体使用及API文档说明
    Linux(SLES)挂载NTFS移动硬盘实践
  • 原文地址:https://www.cnblogs.com/BlueInRed/p/12644165.html
Copyright © 2011-2022 走看看