• Codeforces519 E. A and B and Lecture Rooms


    传送门:>Here<

    题意:询问给出一棵无根树上任意两点$a,b$,求关于所有点$i$,$dist(a,i) = dist(b,i)$的点的数量。要求每一次询问在$O(log n)$的时间复杂度内完成。

    解题思路

    由于在树上求距离,并且还要$O(log n)$,自然会联想到$LCA$。由于边权是$1$,那么点到根的距离就是该点的深度。这个深度可以在$dfs$预处理的过程中处理完成。那么两个点之间的距离就是两个点到根节点的距离减去两点的LCA到根节点距离的两倍。这个随便yy一下就好了。

    得到$a,b$间的距离$D$以后,分类讨论。(设$a$的深度$geq b$的深度)

    (1)若$D$为奇数,则一定不存在任何一个点到$a,b$的距离相等。因此得到$0$.

    (2)若$D$为偶数:

     (一)$a,b$两点分别在$LCA$的两棵子树上。

    ①$a,b$两点深度相同。此时很简单,最近的一个距离相等的点就是$a,b$的$LCA$。也很容易想到$LCA$的祖先也全都符合。但真的只有这些吗?$LCA$的祖先的其他儿子好像也满足诶……$LCA$的其他子树(除了$a,b$)好像也满足诶……因此我们得到结论,在这种情况下得到的答案应当是$n - size[LCA的左子树] - size[LCA的右子树]$

    ②深度不同。那么我们找到中间节点$Mid$,$Mid$里除有$a$的子树外其他子树都符合,并且$Mid$以上的节点都不会符合,因此答案是$size[Mid] - size[有a的那棵子树]$

    (二)$a,b$在同一条链上,即$b$就是$LCA$

    和①类似,中间深度的节点减去含$a$的子树即可

    因此我们要做的不过是在$dfs$的过程中维护好$size$和$dep$。但一直困惑我的是有$a$的那个子树怎么快速得到?答案其实很暴力……再倍增一遍……

    Code

    太坑了!调试了一个多小时竟然是因为$LCA$的预处理dfs中$(1<<i)$打成了$i$,导致$TLE$得莫名其妙。还是$LCA$板子不熟啊……

    /** This Program is written by QiXingZhi **/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define  r  read()
    #define  Max(a,b)  (((a)>(b)) ? (a) : (b))
    #define  Min(a,b)  (((a)<(b)) ? (a) : (b))
    using namespace std;
    typedef long long ll;
    const int N = 100010;
    const int INF = 1061109567;
    inline int read(){
        int x = 0; int w = 1; register int c = getchar();
        while(c ^ '-' && (c < '0' || c > '9')) c = getchar();
        if(c == '-') w = -1, c = getchar();
        while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar();
        return x * w;
    }
    int n,x,y,ans;
    int f[N][30],dep[N],size[N];
    vector <int> G[N];
    inline void AddEdge(int u, int v){
        G[u].push_back(v);
    }
    void LcaInit(int x, int father, int _d){
        dep[x] = _d;
        f[x][0] = father;
        size[x] = 1;
        for(int i = 1; (1<<i) <= _d; ++i){
            f[x][i] = f[f[x][i-1]][i-1];
        }
        int sz,to;
        sz = G[x].size();
        for(int i = 0; i < sz; ++i){
            to = G[x][i];
            if(to == father) continue;
            LcaInit(to,x,_d+1);
            size[x] += size[to];
        }
    }
    inline int GetDepNode(int x, int _d){
        int tmp = x;
        for(int i = 25; i >= 0; --i){
            if(dep[tmp]-(1<<i) < _d) continue;
            tmp = f[tmp][i];
        }
        return tmp;
    }
    inline void LCA(int a, int b){
        if(dep[a] < dep[b]){
            swap(a,b);
        }
        int _a = a, _b = b;
        for(int i = 25; i >= 0; --i){
            if(dep[a]-(1<<i) < dep[b]) continue;
            a = f[a][i];
        }
        int LCA;
        if(a == b){
            LCA = a;
        }
        else{
            for(int i = 25; i >= 0; --i){
                if(f[a][i] == f[b][i]) continue;
                a = f[a][i];
                b = f[b][i];
            }
            LCA = f[a][0];
        }
        int Dist = dep[_a]-dep[LCA]+dep[_b]-dep[LCA];
        if(Dist & 1){
            ans = 0;
            return;
        }
        else{
            if(_b == LCA){
                int dep_Mid = (dep[_a] + dep[_b]) / 2;
                ans = size[GetDepNode(_a,dep_Mid)] - size[GetDepNode(_a,dep_Mid+1)];
            }
            else{
                if(dep[_a] != dep[_b]){
                    int dep_Mid = dep[_a] - (Dist/2);
                    ans = size[GetDepNode(_a,dep_Mid)] - size[GetDepNode(_a,dep_Mid+1)];
                }
                else{
                    ans = n - size[GetDepNode(_a,dep[LCA]+1)] - size[GetDepNode(_b,dep[LCA]+1)];
                }
            }
        }
    }
    int main(){
        n = r;
        for(int i = 1; i < n; ++i){
            x = r, y = r;
            AddEdge(x,y);
            AddEdge(y,x);
        }
        LcaInit(1,0,1);
        int Q = r;
        while(Q--){
            x = r, y = r;
            ans = 0;
            if(x != y){
                LCA(x,y);
                printf("%d
    ",ans);
            }
            else{
                printf("%d
    ",n);
            }
        }
        return 0;
    }
  • 相关阅读:
    《Linux shell变量总结回顾》RHEL6(转)
    20个最受欢迎的Linux命令(转)
    如果看了这篇文章你还不懂傅里叶变换,那就过来掐死我吧(转)
    妻共贫贱难,夫共富贵难
    [置顶] 阅读Oracle官方文档指南
    Java实现 蓝桥杯 算法训练 递归求二项式系数
    Java实现 蓝桥杯 算法训练 数字三角形
    Java实现 蓝桥杯 算法训练 数字三角形
    Java实现 蓝桥杯 算法训练 数字三角形
    Java实现 蓝桥杯 算法训练 数字三角形
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9302038.html
Copyright © 2020-2023  润新知