[Codeforces#519E] A and B and Lecture Rooms

Codeforces题号:#519E

出处: Codeforces

主要算法:最近公共祖先LCA(倍增法)

难度:4.5

思路分析:

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

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

  得到a,b间的距离D以后,分类讨论。(设a的深度>=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

      这时候和(一)①的情况简直一模一样。(想一想,为什么?)

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

代码注意点:

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

Code

/** 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(){
//    freopen(".in","r",stdin);
    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\n",ans);
        }
        else{
            printf("%d\n",n);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qixingzhi/p/9302038.html