标签:ini force 倍增 for 注意 中间 暴力 pen 就是
出处: Codeforces
主要算法:最近公共祖先LCA(倍增法)
难度:4.5
题意:询问给出一棵无根树上任意两点a,b,求关于所有点i,\(dist(a,i) = dist(b,i)\)的点的数量。要求每一次询问在O(log n)的时间复杂度内完成。
由于在树上求距离,并且还要O(log n),自然会联想到LCA。由于边权是1,那么点到根的距离就是该点的深度。这个深度可以在dfs预处理的过程中处理完成。那么两个点之间的距离就是两个点到根节点的距离减去两点的LCA到根节点距离的两倍。这个随便yy一下就好了。
得到a,b间的距离D以后,分类讨论。(设a的深度>=b的深度)
(1)若D为奇数,则一定不存在任何一个点到a,b的距离相等。因此得到0.
(2)若D为偶数:
(一)a,b两点分别在LCA的两棵子树上。
①a,b两点深度相同。此时很简单,最近的一个距离相等的点就是a,b的LCA。也很容易想到LCA的祖先也全都符合。但真的只有这些吗?LCA的祖先的其他儿子好像也满足诶……LCA的其他子树(除了a,b)好像也满足诶……因此我们得到结论,在这种情况下得到的答案应当是\(n - size[LCA的左子树] - size[LCA的右子树]\)。
②深度不同。那么我们找到中间节点Mid,Mid里除有a的子树外其他子树都符合,并且Mid以上的节点都不会符合,因此答案是\(size[Mid] - size[有a的那棵子树]\)
(二)a,b在同一条链上,即b就是LCA
这时候和(一)①的情况简直一模一样。(想一想,为什么?)
因此我们要做的不过是在dfs的过程中维护好size和dep。但一直困惑我的是有a的那个子树怎么快速得到?答案其实很暴力……再倍增一遍……
太坑了!调试了一个多小时竟然是因为LCA的预处理dfs中(1<<i)打成了i,导致TLE得莫名其妙。还是LCA板子不熟啊……
/** This Program is written by QiXingZhi **/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #include <cmath> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int N = 100010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ ‘-‘ && (c < ‘0‘ || c > ‘9‘)) c = getchar(); if(c == ‘-‘) w = -1, c = getchar(); while(c >= ‘0‘ && c <= ‘9‘) x = (x << 3) +(x << 1) + c - ‘0‘, c = getchar(); return x * w; } int n,x,y,ans; int f[N][30],dep[N],size[N]; vector <int> G[N]; inline void AddEdge(int u, int v){ G[u].push_back(v); } void LcaInit(int x, int father, int _d){ dep[x] = _d; f[x][0] = father; size[x] = 1; for(int i = 1; (1<<i) <= _d; ++i){ f[x][i] = f[f[x][i-1]][i-1]; } int sz,to; sz = G[x].size(); for(int i = 0; i < sz; ++i){ to = G[x][i]; if(to == father) continue; LcaInit(to,x,_d+1); size[x] += size[to]; } } inline int GetDepNode(int x, int _d){ int tmp = x; for(int i = 25; i >= 0; --i){ if(dep[tmp]-(1<<i) < _d) continue; tmp = f[tmp][i]; } return tmp; } inline void LCA(int a, int b){ if(dep[a] < dep[b]){ swap(a,b); } int _a = a, _b = b; for(int i = 25; i >= 0; --i){ if(dep[a]-(1<<i) < dep[b]) continue; a = f[a][i]; } int LCA; if(a == b){ LCA = a; } else{ for(int i = 25; i >= 0; --i){ if(f[a][i] == f[b][i]) continue; a = f[a][i]; b = f[b][i]; } LCA = f[a][0]; } int Dist = dep[_a]-dep[LCA]+dep[_b]-dep[LCA]; if(Dist & 1){ ans = 0; return; } else{ if(_b == LCA){ int dep_Mid = (dep[_a] + dep[_b]) / 2; ans = size[GetDepNode(_a,dep_Mid)] - size[GetDepNode(_a,dep_Mid+1)]; } else{ if(dep[_a] != dep[_b]){ int dep_Mid = dep[_a] - (Dist/2); ans = size[GetDepNode(_a,dep_Mid)] - size[GetDepNode(_a,dep_Mid+1)]; } else{ ans = n - size[GetDepNode(_a,dep[LCA]+1)] - size[GetDepNode(_b,dep[LCA]+1)]; } } } } int main(){ // freopen(".in","r",stdin); n = r; for(int i = 1; i < n; ++i){ x = r, y = r; AddEdge(x,y); AddEdge(y,x); } LcaInit(1,0,1); int Q = r; while(Q--){ x = r, y = r; ans = 0; if(x != y){ LCA(x,y); printf("%d\n",ans); } else{ printf("%d\n",n); } } return 0; }
[Codeforces#519E] A and B and Lecture Rooms
标签:ini force 倍增 for 注意 中间 暴力 pen 就是
原文地址:https://www.cnblogs.com/qixingzhi/p/9302038.html