ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[CF519E] A and B and Lecture Rooms - LCA

2021-01-26 08:32:19  阅读:184  来源: 互联网

标签:fa int siz DEP dep CF519E LCA Lecture rela


[CF519E] A and B and Lecture Rooms - LCA

Description

给定一棵树,有 \(m\) 组询问,每次给定两个点 \(u,v\),问到 \(u,v\) 距离相等的点有多少个。

Solution

一定是连接 \(u,v\) 的路径的中点以及它所发出的其它子树。

以下设 \(LCA(u,v)=l, MID(u,v)=c\)。

如果 \(l=c\),那么砍掉与 \(u,v\) 有关的两棵子树即可。

如果 \(l \neq c\),假设 \(DEP(u)>DEP(v)\),则中点的子树砍掉与 \(u\) 有关可的一棵子树即可。

问题 1:到底怎么求中点?

中点存在的充要条件为 \(2|(DEP(u)+DEP(v))\),如果中点存在,那么它的深度,是可以求出的:直链的情况为 \(\frac {DEP(u)+DEP(v)} 2\),曲链的情况下,设 \(len = DEP(u)+DEP(v)-2DEP(l)\),假设 \(DEP(u) \ge DEP(v)\),则 \(MID\) 的深度为 \(DEP(u)-\frac {len} 2\)。求出深度后,我们从较深点倍增向上跳即可。

问题 2:怎样砍掉点 \(p\) 与点 \(q\) 相关的一棵子树?

我们将 \(q\) 倍增向上跳,使它成为 \(p\) 的孩子,那么此时的深度显然是 \(DEP(p)+1\)。

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1000005;

int n, m;
vector<int> g[N];
int dep[N], fa[N][20], siz[N];

// 树,倍增 LCA,求路径长度

void dfs(int p, int from)
{
    siz[p] = 1;
    for (auto q : g[p])
    {
        if (q != from)
        {
            dep[q] = dep[p] + 1;
            fa[q][0] = p;
            dfs(q, p);
            siz[p] += siz[q];
        }
    }
}

void presolve()
{
    dep[1] = 1;
    dfs(1, 0);
    for (int j = 1; j < 20; j++)
        for (int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
}

int lca(int p, int q)
{
    if (dep[p] < dep[q])
        swap(p, q);
    for (int i = 19; i >= 0; i--)
        if (dep[fa[p][i]] >= dep[q])
            p = fa[p][i];
    for (int i = 19; i >= 0; i--)
        if (fa[p][i] != fa[q][i])
        {
            p = fa[p][i];
            q = fa[q][i];
        }
    if (p != q)
        return fa[p][0];
    return p;
}

// 跳跃,到某一确定深度

int jump(int p, int depth)
{
    for (int i = 19; i >= 0; i--)
        if (dep[fa[p][i]] >= depth)
            p = fa[p][i];
    return p;
}

// 求中点

bool check_mid(int u, int v)
{
    return (dep[u] + dep[v]) % 2 == 0;
}

int mid(int u, int v)
{
    int l = lca(u, v);
    if (l == v)
    {
        int depth = (dep[u] + dep[v]) / 2;
        return jump(u, depth);
    }
    else
    {
        int len = dep[u] + dep[v] - 2 * dep[l];
        int depth = dep[u] - len / 2;
        return jump(u, depth);
    }
}

// 求相关孩子

int get_rela_child(int p, int q)
{
    return jump(q, dep[p] + 1);
}

// 主程序

signed main()
{

    ios::sync_with_stdio(false);

    cin >> n;
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    presolve();

    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        if (dep[u] < dep[v])
            swap(u, v);
        int l = lca(u, v);
        int ans = 0;
        if (u == v)
        {
            ans = n;
        }
        else if (check_mid(u, v))
        {
            int c = mid(u, v);
            if (l == c)
            {
                int rela_u = get_rela_child(c, u);
                int rela_v = get_rela_child(c, v);
                ans = n - siz[rela_u] - siz[rela_v];
            }
            else
            {
                int rela_u = get_rela_child(c, u);
                ans = siz[c] - siz[rela_u];
            }
        }
        cout << ans << endl;
    }
}

标签:fa,int,siz,DEP,dep,CF519E,LCA,Lecture,rela
来源: https://www.cnblogs.com/mollnn/p/14328522.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有