Codeforces 519E A and B and Lecture Rooms [倍增法LCA]

时间:2021-02-14 19:09:35

题意:

给你一棵有n个节点的树,给你m次询问,查询给两个点,问树上有多少个点到这两个点的距离是相等的。树上所有边的边权是1。

思路:

很容易想到通过记录dep和找到lca来找到两个点之间的距离,然后分情况讨论。

一开始困扰我的问题是如果lca不是正中间的点,如何在比较低的复杂度的层面上求解中点。

倍增法lca不光可以在logn的时间复杂度内查询某两个点的lca,还可以实现在logm的时间复杂度能查询某个节点的第m个父亲节点。

算法的核心是用二进制的运算来实现查询。

#include<bits/stdc++.h>
#define N 100050
using namespace std;
int n;
struct edge{
    int id;
    edge *next;
};
edge edges[N<<1];
edge *adj[N];
int ednum;
int dep[N],rt[25][N],siz[N];
inline void add_Edge(int a,int b){
    edge *tmp=&edges[ednum++];
    tmp->id=b;
    tmp->next=adj[a];
    adj[a]=tmp;
}
void dfs(int pos,int deep){
    dep[pos]=deep;
    siz[pos]=1;
    for(edge *it=adj[pos];it;it=it->next){
        if(!dep[it->id]){
            rt[0][it->id]=pos;
            dfs(it->id,deep+1);
            siz[pos]+=siz[it->id];
        }
    }
}
void prelca(){
    for(int i=1;i<=20;i++){
        for(int j=1;j<=n;j++){
            rt[i][j]=rt[i-1][j]==-1?-1:rt[i-1][rt[i-1][j]];
        }
    }
}
int LCA(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    for(int i=0;i<21;i++){
        if((dep[u]-dep[v])>>i&1){
            u=rt[i][u];
        }
    }
    if(u==v)return u;
    for(int i=19;i>=0;i--){
        if(rt[i][u]!=rt[i][v]){
            u=rt[i][u];
            v=rt[i][v];
        }
    }
    return rt[0][u];
}
int jump(int pos,int num){
    for(int i=0;i<21;i++){
        if(num>>i&1){
            pos=rt[i][pos];
        }
    }
    return pos;
}
void solve(int u,int v){
    if(u==v){
        printf("%d\n",n);
        return;
    }
    if(max(dep[u],dep[v])-min(dep[u],dep[v])&1){
        printf("0\n");
        return;
    }
    int anc=LCA(u,v);
    //printf("anc=%d\n",anc);
    if(dep[u]==dep[v]){
        int ans=n;
        ans-=siz[jump(u,dep[u]-dep[anc]-1)];
        ans-=siz[jump(v,dep[u]-dep[anc]-1)];
        printf("%d\n",ans);
    }
    else{
        int l=dep[u]+dep[v]-2*dep[anc];
        if(dep[u]<dep[v])swap(u,v);
        int ans=siz[jump(u,l/2)];
        ans-=siz[jump(u,l/2-1)];
        printf("%d\n",ans);
    }
}
int main()
{
    scanf("%d",&n);
    memset(rt,-1,sizeof(rt));
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        add_Edge(a,b);
        add_Edge(b,a);
    }
    dfs(1,1);
    prelca();
    //printf("*%d\n",jump(2,0));
    int m;
    scanf("%d",&m);
    for(int  i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        solve(a,b);
    }
    return 0;
}
/*
5
1 5
1 2
2 3
2 4
5
1 5
2 5
1 1
2 2
3 4
*/