CF519E 【A and B and Lecture Rooms】

前言:

你可能需要用到的前置知识点: 倍增求 (或者说是树上倍增?)

正文

题目大意:

给定一棵树,以及 个询问,每次询问的形式是给定两个点 ,求有多少个点 满足

题目不难 ,但是要分类讨论清楚也不是那么容易。

无根树,我们假定 号点为根即可。

下面约定概念:

表示 点的深度。 表示 为根的子树的大小。

然后我们开始分类讨论(下面假定 ):

  • .

输出点的个数即可。所有点均满足条件。

  • .

意味着 的祖先节点。

不难发现,倘若 中间有偶数个点的话,是没有答案的,输出

倘若 , 中间有奇数个点,那么答案就是 的路径的中点 以及 的所有儿子(除了 所在的那一整个子树)。可以通过倍增的方法求出 点以及 所在的那一个子树的根节点。

  • &&

这个说明 就是 的路径中点,那么除了在以 为根的子树中的 所在的子树以及 所在的子树,其它的点都可以。

  • . &&

假若路径中有偶数个点,很显然没有答案。

假若是奇数,取路径中点 求子树 - 所在的那一整个子树大小 大小即可。

至于具体写法的一些小 就放在代码里面了。

// Problem: CF519E A and B and Lecture Rooms
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// Powered by CP Editor (https://github.com/cpeditor/cpeditor)

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 50;
int start[MAXN] , tot = 0 , siz[MAXN] , n , parent[MAXN][20] , dep[MAXN];

struct Edge {
    int next,to;
} edge[MAXN << 1];

inline int read() {
    int x = 0 , flag = 1;
    char ch = getchar();
    for( ; ch > '9' || ch < '0' ; ch = getchar()) if(ch == '-') flag = -1;
    for( ; ch >= '0' && ch <= '9' ; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
    return x * flag;
}

void add(int from,int to) {
    edge[++tot].next = start[from];
    edge[tot].to = to;
    start[from] = tot;
    return ;
}

int DFS(int x , int from) {//预处理Parent数组以及深度数组
    siz[x] = 1; parent[x][0] = from; dep[x] = dep[from] + 1;
    for(int i = 1 ; i <= log2(dep[x]) ; i ++) 
    parent[x][i] = parent[parent[x][i - 1]][i - 1];
    for(int i = start[x] ; i ; i = edge[i].next) {
        int to = edge[i].to;
        if(to == from) continue;
        siz[x] += DFS(to , x);
    }
    return siz[x];
}

int LCA(int x,int y) {//倍增求LCA的模板
    if(dep[x] > dep[y]) swap(x,y);
    for(int i = log2(dep[y] - dep[x]) ; i >= 0 ; i --)
        if(dep[parent[y][i]] >= dep[x]) y = parent[y][i];
    if(x == y) return x;
    for(int i = log2(dep[x]) ; i >= 0 ; i --)
    if(parent[x][i] != parent[y][i]) x = parent[x][i] , y = parent[y][i];
    return parent[x][0];
}

int GetFa(int x,int Deep) {
    //这是给定深度,然后倍增求一个距离 x 点距离为 k 的祖先 
    while(Deep) {
        int d = log2(Deep);
        x = parent[x][d];
        Deep -= (1 << d);
    }
    return x;
}

int GetLas(int x,int Fa) {
    //这是给定一个点,然后倍增求一个点所在这个点中的哪个子树(求所在子树的根)
    for(int i = log2(dep[x]) ; i >= 0 ; i --)
    if(dep[parent[x][i]] > dep[Fa]) x = parent[x][i];
    return x;
}

void solve(int x,int y) {
    if(dep[x] > dep[y]) swap(x , y);
    if(x == y) {printf("%d\n",n); return ;}
    if(LCA(x,y) == x) {
        if((dep[y] - dep[x]) & 1) {printf("%d\n",0); return ;}//这表示是偶数个点可以画图知道
        int Fa = GetFa(y,(dep[y] - dep[x]) >> 1) , son = GetFa(y , ((dep[y] - dep[x]) >> 1) - 1);//这里Fa就是中点
        printf("%d\n",siz[Fa] - siz[son]);
        return ;
    }
    int L = LCA(x,y) , Road = dep[x] + dep[y] - dep[L] * 2;//故意不+1的....
    if(dep[x] - dep[L] == dep[y] - dep[L]) {//对应情况3
        int LS,RS;
        LS = GetLas(x , L) , RS = GetLas(y , L);//分别求出所在的子树
        int Ans = 0;
        Ans = n - siz[LS] - siz[RS];
        printf("%d\n",Ans);
        return ;
    }
    if(Road & 1) {printf("%d\n",0) ; return ;} //偶数个点
    else {
        int Fa = GetFa( y , (Road >> 1)) , son = GetLas(y , Fa);
        printf("%d\n",siz[Fa] - siz[son]);//对应情况4
        return ;
    }
    return ;
}

int main() {
    n = read();
    for(int i = 1 ; i <= n - 1; i ++) {
        int u = read() , v = read();
        add(u , v) ; add(v , u);
    }
    DFS(1,0);
    int Q = read();
    while(Q --) {
        int u = read() , v = read();
        solve(u,v);
    }
    return 0;
}