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

题意:

给你一棵有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
*/
时间: 2024-07-28 22:03:46

Codeforces 519E A and B and Lecture Rooms [倍增法LCA]的相关文章

CodeForces 519E A and B and Lecture Rooms(倍增)

A and B are preparing themselves for programming contests. The University where A and B study is a set of rooms connected by corridors. Overall, the University has n rooms connected by n - 1corridors so that you can get from any room to any other one

codeforces 519E A and B and Lecture Rooms LCA倍增

Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 519E Description A and B are preparing themselves for programming contests. The University where A and B study is a set of rooms connected

[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预处理的过程中处理完成.那么两个点之间的距离就是两个点到根节点

Codeforces 519E A and B and Lecture Rooms LCA

题目链接:点击打开链接 题意: 给定n个点的树. 下面n-1行给出树 Q个询问. 每次询问 (u,v)问树上有多少个点到u点距离=到v点距离 思路: 首先这两个点的距离必须是偶数,若为奇数答案就是0 然后用lca找到中间节点即可. trick : u==v ans = n #include"cstdio" #include"iostream" #include"queue" #include"algorithm" #inclu

Codefources 519E. A and B and Lecture Rooms LCA

简单LCA: 求树上距离给定两个点a,b距离相等的点有多少个 先预处理出每个节点的孩子个数sum[x],求出a,b的LCA,根据深度就可以知道两个点的距离,距离为偶数的有解.... 根据lca在a,b之间的位置不同分情况讨论: 设a与lca距离为 ha , b与lca距离为 hb 1:lca在a,b正中间既a,b分别属于lca的两个子树中, 结果为: n-sum[ a往上距离lca ha-1 的点] - sum[ b往上距离lca hb-1 的点] 2:a,b两个点相对lca一上一下. c:a,

CF 519E(树上倍增求lca)

传送门:A and B and Lecture Rooms 题意:给定一棵树,每次询问到达点u,v距离相等的点有多少个. 分析:按情况考虑: 1.abs(deep[u]-deep[v])%2==1时,必定不存在到达u,v距离相等的点. 2.如果deep[u]==deep[v]时,ans=n-num[lca(u,v)u在的儿子树]-num[lca(u,v)v在的儿子树]. 3.如果deep[u]!=deep[v]时,在u到v的路径中找出到达u,v相等的节点x,则ans=num[x]-num[u在的

CodeForces 519E 树形DP A and B and Lecture Rooms

给出一棵树,有若干次询问,每次询问距两个点u, v距离相等的点的个数. 情况还挺多的,少侠不妨去看官方题解.^_^ 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using namespace std; 7 8 const int maxn = 100000 + 10; 9 10

Codeforces Round #294 Div2 E(A and B and Lecture Rooms)

Problem 给一棵树,含有N个节点,N?1条边.进行M次查询,每次给定两个节点x,y,问树上有多少个节点到x,y的距离相同. Limits TimeLimit(ms):2000 MemoryLimit(MB):256 N,M∈[1,105] x,y∈[1,N] Look up Original Problem From here Solution 求出a,b两个节点的lca,再找到lca?>a或lca?>b上的某个节点v使得dis(v,a)=dis(v,b).分v=lca,v∈lca?&g

Codeforces Round #287 D.The Maths Lecture

The Maths Lecture 题意:求存在后缀Si mod k =0,的n位数的数目.(n <=1000,k<=100); 用f[i][j]代表 长为i位,模k等于j的数的个数. 可以用 f[i+1][(t*10i+j)%k]=∑f[i][j]+(j==0),(t*10i+j)%k!=0;动态规划 这样可以求出所有f[n][i] i>0 的值. 最后用9*10^(n-1)-∑f[n][i] 就可以得到 答案 #include <bits/stdc++.h> using