2016.10.27 Tayz T3 范围(倍增LCA+伪.树的直径)

本文介绍了一种求解树的直径的高效算法,并通过一个具体的竞赛题目来演示如何使用该算法解决实际问题。文章详细解释了算法原理,包括利用深度优先搜索(DFS)计算节点深度、倍增法求最近公共祖先(LCA)以及确定树中两个最远节点间的距离。

【问题描述】
蒟蒻 XT 在机房用过 n 个电脑,所有的电脑从 1 到 n 编号,并且这 n 个电脑之间的网络连接
形成树形结构(边权为 1)。蒟蒻 XT 请神犇 X 把 n 个电脑归入了 k 个局域网(k≤
2
n
),局域网
从 1 到 k 编号。保证一个电脑不会在 2 个局域网中出现。而神犇 X 要问蒟蒻 XT 的则是在同一局
域网内最远的两台电脑之间的距离。蒟蒻 XT 不会算,找到你帮忙。
【输入格式】
第一行为 n,k;
接下来 n 行,内行两个正整数,pi和 fai,分别表示第 i 号电脑归属于哪个局域网和第 i 号电脑
的父亲节点。
若 fai为 0,则表示 i 号电脑是根,数据保证每个局域网内至少两个有电脑。
【输出格式】
k 行每行一个整数,表示第 i 个局域网内最远的两台电脑之间的距离。
【输入样例】
6 2
1 3
2 1
1 0
2 1
2 1
1 5
【输出样例】
3
2
【数据说明】
对于 30% 的数据 n ≤ 100;
对于 50% 的数据 n ≤ 50000;
对于 100%的数据 n ≤ 200000。

今天在搞LCA想找水题练练手,跑去问小兔子,小兔子曰:”tayz T3。”,看了数据范围感觉不可做,小兔子曰:”LCA不打倍增和咸鱼有什么区别?”

10.26号刚搞了树的直径,27号胡策就考了这个题….
考场上直接上暴力两边BFS搜的树的直径,结果贼慢….

进入正题
树的直径,树的直径是指树的最长简单路。
怎么求?
在一个树上,从任意一个点出发,跑最长路跑到最远的点f,从f再跑最长路,跑到最远的点t,f到t之间的路径就是树的直径。

因为这个树上的边权都是1…所以路径就和深度有关啊。
从根开始往下DFS处理出每个点的深度,u节点到v节点的距离就是deep[u]+deep[v]-2*deep[LCA(u,v)];
正确性自己证一下。

对于k个局域网,我们可以用一个vector存。
vector[i][j]代表第i个局域网的第j个节点。
我们一开始随便找一个局域网i中的节点,求它和局域网内所有节点的距离,跑出比当前maxx长的距离,就把最远点更新一下,然后再求最远点到局域网i内所有节点的最远距离

#include<algorithm>
#include<cstdio>
#include<vector>
#include<cmath>
#include<cstdlib>
using namespace std;
const int maxn=200000+50;
int p[maxn][30];
struct Edge
{
    int to;
    int next;
}edge[maxn<<2];
int n,k;
int tot;
int head[maxn],deep[maxn],fa[maxn];
void add(int f,int t)
{
    edge[++tot]=(Edge){t,head[f]};
    head[f]=tot;
}
void dfs1(int f,int t)
{
    deep[t]=deep[f]+1;
    for(int i=head[t];i;i=edge[i].next)
    {
        Edge e=edge[i];
        if(!deep[e.to])
        {
            p[i][0]=t;
            dfs1(t,e.to);
        }
    }
}
void clr()
{
    for(int j=1;j<=25;j++)
    {
        for(int i=1;i<=n;i++)
        {
            if(p[i][j-1])
                p[i][j]=p[p[i][j-1]][j-1];
        } 
    }

}

int lca(int a,int b)
{
    int x=a,y=b;
    if(deep[a]<deep[b])
        swap(a,b);
    for(int j=25;j>=0;j--)
    {
        if(deep[p[a][j]]>=deep[b])
        a=p[a][j];
    }
    if(a==b)
    return abs(deep[x]-deep[y]);//一条链。 
    for(int j=25;j>=0;j--)
    {
        if(p[a][j]!=p[b][j])
            a=p[a][j],b=p[b][j];
    }
    return deep[x]+deep[y]-2*deep[p[a][0]];
}
int lan[maxn];
vector<int> v[maxn];
int main()
{
    int __size__ = 1024 << 15;
    char * __p__ = (char *) malloc(__size__) + __size__;
    __asm__("movl %0, %%esp\n" :: "r"(__p__));
    freopen("cowpol.in","r",stdin);
    freopen("cowpol.out","w",stdout);
    int root;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        int b,a;
        scanf("%d%d",&b,&a);
        v[b].push_back(i);
        if(a==0)
            root=i;
        add(a,i);
    }
    dfs1(200050,root);
    clr();
    for(int i=1;i<=k;i++)
    {
        int poi=0,maxx=0;
        for(int j=0;j<v[i].size();j++)
        {
            int hah=lca(v[i][0],v[i][j]);
            if(hah>maxx)
            {
                maxx=hah;
                poi=v[i][j];
            } 
        }
        maxx=0;
        for(int j=0;j<v[i].size();j++)
        {
            int hah=lca(poi,v[i][j]);
            if(hah>maxx)
                maxx=hah;
        }
        printf("%d\n",maxx);    
    }
    return 0;
}
的公共祖先(LCA),即最近公共祖先,在一颗有根中,点 x 上面(深度小于 x 的深度)的点叫做点 x 的祖先,点 x 与点 y 的众多祖先中,相同的祖先叫做 x 与 y 的公共祖先,这样的公共祖先至少有一个即根节点,而在众多公共祖先中,最接近(深度最大)点 x 和点 y 的那个就是 x 与 y 的最近公共祖先(LCA)[^1]。 求解 LCA 的基本思路是分治法,两个节点的相对位置关系有三种情况:p 和 q 在同一侧某个子中;p 和 q 在两侧不同子中;边界情况,p、q 中有一个节点不存在在该中[^2]。 在上差分中,若要统计 x 到 y 路线上的点,定义 d[i] 为点 i 的权值,那么 d[x]++,d[y]++,d[lca(x,y)]--,d[father[lca(x,y)]]--,点 u 的经过次数就是以 u 为根的子权值之和[^3]。 还可以设 f[i][j] 代表从编号为 i 的节点向上走 2^j 步到达的节点编号为 f[i][j] 。若该节点不存在,则令 f[i][j]=0 (令 f[i][j]= -1 也可以,但是需要注意以 f[i][j] 作下标可能越界)[^4]。 ### 代码示例 以下是一个简单的 Python 示例,用于求解二叉中两个节点的最近公共祖先: ```python class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None def lowestCommonAncestor(root, p, q): if not root or root == p or root == q: return root left = lowestCommonAncestor(root.left, p, q) right = lowestCommonAncestor(root.right, p, q) if left and right: return root return left if left else right # 示例用法 root = TreeNode(3) root.left = TreeNode(5) root.right = TreeNode(1) root.left.left = TreeNode(6) root.left.right = TreeNode(2) root.right.left = TreeNode(0) root.right.right = TreeNode(8) root.left.right.left = TreeNode(7) root.left.right.right = TreeNode(4) p = root.left q = root.right res = lowestCommonAncestor(root, p, q) print(res.val) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值