题解 CF519E 【A and B and Lecture Rooms】
A and B and Lecture Rooms
https://ac.nowcoder.com/acm/problem/110856
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; }
闲人碎语 文章被收录于专栏
刊载算法学习笔记以及一些题解