LCA

http://codeforces.com/contest/519/problem/E

题意:

给你一个无根树,q个询问,每次输入两个节点,问到两个节点距离相同的点有几个

tip:

设1为根,dfs记录每个点子树的节点数量,每个点的高度,
倍增,找到u,v的lca,根据三个点的高度可以知道在从u到v的链上到两个点距离相等的点的高度,
if(lca不是这个中间点且长度是偶数,那么这个中间点的所有子树除了这个链上的所有节点都是等距离点)
if(奇数)无解
if(lca是中间点,那除了这条链和uv子树,其他的都是等距离点)

假设u是深度比较深的点,那么从u往上找那个能算出来的长度,就是中间点,这个找的过程,因为有倍增,所以时间复杂度降到 logn

#include <cstdio>
#include <iostream>
#include <cstring>
#define LL long long
using namespace std;
const int maxn = 2e5+10;
int n,m,root,high[maxn],child[maxn],tot;
int first[2*maxn],father[maxn][35];
struct node{
    int v,next;
}edges[2*maxn];
void add(int u,int v){
    edges[tot].v = v;edges[tot].next = first[u];first[u] = tot++;
    edges[tot].v = u;edges[tot].next = first[v];first[v] = tot++;
}

void dfs(int root,int fa,int step){
    child[root] = 1;
    high[root] = step;
    for(int k = first[root] ; k != -1; k = edges[k].next){
        if(edges[k].v == fa)    continue;
        father[edges[k].v][0] = root;
        dfs(edges[k].v,root,step+1);
        child[root]+=child[edges[k].v];
    }
}
void bz(){
    for(int j = 1; j <= 30 ;j++)
        for(int i = 1; i<= n ;i++)
            if(father[i][j-1]!=-1)
                father[i][j] = father[father[i][j-1]][j-1];
            else
                father[i][j] = -1;
}

int fin(int a,int h){
    for(int i = 0 ;i < 30 ;i++)
        if((1<<i) & h) a = father[a][i];
    return a;
}

int lca(int x,int y){
    int dc = high[x]-high[y];
    for(int i = 0 ;i < 30 ;i++)
        if((1<<i) & dc) x = father[x][i];
    if(x == y)  return x;
    for(int i = 29 ; i >= 0 ; i--)
        if(father[x][i] != father[y][i]){
            x = father[x][i];
            y = father[y][i];
        }
    x = father[x][0];
    return x;
}

void init(){
    scanf("%d",&n);
    tot = 0;
    memset(first,-1,sizeof(first));
    LL sum = 0;
    for(int i = 0 ; i < n-1 ; i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    root = 1;
}

void sov(){
    scanf("%d",&m);
    for(int i = 0; i < m ;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        if(high[a]<high[b])
            swap(a,b);
        if(a==b){
            printf("%d\n",n);
            continue;
        }
        int inc = lca(a,b),ans;
        if(high[a] != high[b]){
            if((high[a]-high[b])%2){
                printf("0\n");
                continue;
            }
            int anshigh = (high[a]-high[b])/2+high[inc];
            int now = fin(a,high[a]-anshigh-1),ok = fin(a,high[a]-anshigh);
            ans = child[ok]- child[now];
        }
        else{
           int now = fin(a,high[a]-high[inc]-1),ok = fin(b,high[b]-high[inc]-1);
           ans = n-child[now]-child[ok];
        }
        printf("%d\n",ans);
    }
}

int main(){
    init();
    father[root][0] = -1;
    dfs(root,-1,1);
    bz();
    sov();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值