F. Timofey and Black-White Tree
Timofey came to a famous summer school and found a tree on $n$ vertices. A tree is a connected undirected graph without cycles.
Every vertex of this tree, except $c_0$, is colored white. The vertex $c_0$ is colored black.
Timofey wants to color all the vertices of this tree in black. To do this, he performs $n - 1$ operations. During the $i$-th operation, he selects the vertex $c_i$, which is currently white, and paints it black.
Let's call the positivity of tree the minimum distance between all pairs of different black vertices in it. The distance between the vertices $v$ and $u$ is the number of edges on the path from $v$ to $u$.
After each operation, Timofey wants to know the positivity of the current tree.
Input
The first line contains the integer $t$ ($1 \le t \le 10^4$) — the number of testcases.
The first line of each testcase contains the integers $n, c_0$ ($2 \le n \le 2 \cdot 10^5$, $1 \le c_0 \le n$) — the number of vertices in the tree and index of the initial black vertex.
The second line of each testcase contains $n - 1$ unique integers $c_1, c_2, \dots, c_{n-1}$ ($1 \le c_i \le n$, $c_i \ne c_0$), where $c_i$ is the vertex which is colored black during the $i$-th operation.
Each of the next $n - 1$ row of each testcase contains the integers $v_i, u_i$ ($1 \le v_i, u_i \le n$) — edges in the tree.
It is guaranteed that the sum of $n$ for all testcases does not exceed $2 \cdot 10^5$.
Output
For each testcase, print $n - 1$ integer on a separate line.
The integer with index $i$ must be equal to positivity of the tree obtained by the first $i$ operations.
Example
input
6 6 6 4 1 3 5 2 2 4 6 5 5 3 3 4 1 3 4 2 4 1 3 3 1 2 3 1 4 10 3 10 7 6 5 2 9 8 1 4 1 2 1 3 4 5 4 3 6 4 8 7 9 8 10 8 1 8 7 3 7 5 1 2 4 6 1 2 3 2 4 5 3 4 6 5 7 6 9 7 9 3 1 4 2 6 8 5 4 1 8 9 4 8 2 6 7 3 2 4 3 5 5 4 10 2 1 8 5 10 6 9 4 3 7 10 7 7 8 3 6 9 7 7 6 4 2 1 6 7 5 9 2
output
3 2 1 1 1 3 1 1 3 2 2 2 2 2 1 1 1 4 2 2 1 1 1 5 1 1 1 1 1 1 1 4 3 2 2 1 1 1 1 1
Note
In the first testcase, after the second operation, the tree looks like this:
The distance between vertices $1$ and $6$ is $3$, the distance between vertices $4$ and $6$ is $3$, the distance between vertices $1$ and $4$ is $2$. The positivity of this tree is equal to the minimum of these distances. It equals $2$.
In the third testcase, after the fourth operation, the tree looks like this:
The positivity of this tree is $2$.
解题思路
这题的难度感觉超过我的能力水平了,看了半天都不是很理解给出的做法,包括时间复杂度的证明也是很懵。
不难想到暴力的做法是每染黑一个点,就从这个点开始bfs,然后枚举所有已染黑的点到这个点的最短距离,并维护一个全局的最小值,时间复杂度是$O(n^2)$。实际上给出的做法也是暴力的思路,不过在bfs的时候加了剪枝,最终的时间复杂度变成了$O(n \sqrt{n})$。
在下面讲述的方法中$\text{dist}$数组的定义与原先的定义不同,原先bfs中$\text{dist}[v]$表示从始点(树根)到节点$v$的最小值,下面的方法中$\text{dist}[v]$表示离节点$v$最近的已被染黑的点的距离。注意这里的最近不是指最新被染黑的节点,而是所有已经染黑的节点中,距离它最近的那个节点。
大致的流程为每染黑一个点$c_i$,把$c_i$加入队列进行bfs。在bfs的过程中假设当前的节点为$u$,与$u$相邻的节点都记作$v$,只有$\text{dist}[v] > \text{dist}[u] + 1$并且$\text{dist}[u] + 1 < \text{ans}$才更新$\text{dist}[v]$并把$v$加入队列,其中$\text{ans}$记录的是任意两个被染黑的点间的最小距离,显然$\text{ans}$只会变小不会变大。最后是答案的输出,每次遍历到$c_i$,那么就对$\text{ans}$和$\text{dist}[c_i]$取最小值,再输出$\text{ans}$(先输出答案再染黑$c_i$)。首先$\text{ans}$已经记录了之前所有被染黑的点间的最小距离,在染黑$c_i$后本质上是想知道$c_i$到其他已染黑的点最短距离,然后更新$\text{ans}$,而$\text{dist}[c_i]$就表示所有已染黑的点中距离$c_i$的最短距离,因此在染黑$c_i$前取$\text{ans} = \min\{ {\text{ans}, \text{dist}[c_i]} \}$就可以达到此目的。
下面重点讲讲剪枝。首先是只有$\text{dist}[v] > \text{dist}[u] + 1$才更新$\text{dist}[v]$。这是很显然的,问题就是如果$v$不加入队列那么以$v$为根的子树都得不到更新。实际上这些节点都不需要更新,因为这些节点之前都是通过$\text{dist}[v]$更新得到最小距离的,如果要以$\text{dist}[u] + 1$更新距离,明显不如$\text{dist}[v]$小,因此这些节点是不会被更新的。
然后是$\text{dist}[u] + 1 < \text{ans}$才更新$\text{dist}[v]$。首先补充一个概念,在长度为$n$的数轴中加入$\sqrt{n}$个点,那么任意两点间的最小距离所能够取到的最大值为$\sqrt{n}$(准确来说是$\sqrt{n}$这个量级),即当所有点都按照间隔$\sqrt{n}$来排列时取到。这个结论在树中也成立,在一颗$n$个节点的树中选择$\sqrt{n}$个节点,所能取到的任意两个节点的最小距离的最大值为$\sqrt{n}$量级。这个结论也是参考别人的说法,我感觉不是很显然,也许可以通过dfs序来证明,这里就默认这个结论是正确的。意味着当已经染黑了$\sqrt{n}$个节点,此时的$\text{ans}$必然不超过$\sqrt{n}$(量级)。
因此按照上面的算法在枚举完前$\sqrt{n}$个节点时的时间复杂度为$O(n \sqrt{n})$,枚举剩下的节点的时间复杂度也是$O(n \sqrt{n})$,因此总的时间复杂度就是$O(n \sqrt{n})$。下面来分析枚举剩下的节点的时间复杂度。bfs的时间复杂度取决于所有弹出队列的节点的度数,因此可以计算在枚举剩下的节点的过程中,所有节点能够入队的次数。因为枚举完前$\sqrt{n}$个节点后,任意两个黑色节点的最短距离不超过$\sqrt{n}$,而要入队的只有白色节点,在$\text{dist}[u] + 1 < \text{ans}$才更新$\text{dist}[v]$的限制下,可以发现每个节点最多只能入队$\sqrt{n}$次,因此所有节点入队的总次数就是$O(n \sqrt{n})$的量级,因此时间复杂度也是$O(n \sqrt{n})$。
可以发现这总做法被分成了两个部分,前部分是枚举前$\sqrt{n}$个节点,后部分是枚举剩下的节点,而这两部分的时间复杂度均为$O(n \sqrt{n})$,这种做法被称为根号分治。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 2e5 + 10, M = N * 2; 5 6 int c[N]; 7 int head[N], e[M], ne[M], idx; 8 int dist[N]; 9 int q[N], hh, tt; 10 int ans; 11 12 void add(int v, int w) { 13 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 14 } 15 16 void bfs(int src) { 17 hh = 0, tt = -1; 18 q[++tt] = src; 19 dist[src] = 0; 20 while (hh <= tt) { 21 int t = q[hh++]; 22 for (int i = head[t]; i != -1; i = ne[i]) { 23 if (dist[e[i]] > dist[t] + 1 && dist[t] + 1 < ans) { 24 dist[e[i]] = dist[t] + 1; 25 q[++tt] = e[i]; 26 } 27 } 28 } 29 } 30 31 void solve() { 32 int n; 33 scanf("%d", &n); 34 for (int i = 0; i < n; i++) { 35 scanf("%d", c + i); 36 } 37 idx = 0; 38 memset(head, -1, sizeof(head)); 39 for (int i = 0; i < n - 1; i++) { 40 int v, w; 41 scanf("%d %d", &v, &w); 42 add(v, w), add(w, v); 43 } 44 ans = n + 1; 45 memset(dist, 0x3f, sizeof(dist)); 46 for (int i = 0; i < n; i++) { 47 ans = min(ans, dist[c[i]]); 48 if (i) printf("%d ", ans); 49 bfs(c[i]); 50 } 51 printf("\n"); 52 } 53 54 int main() { 55 int t; 56 scanf("%d", &t); 57 while (t--) { 58 solve(); 59 } 60 61 return 0; 62 }
补充个dfs实现的AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 2e5 + 10, M = N * 2; 5 6 int c[N]; 7 int head[N], e[M], ne[M], idx; 8 int dist[N]; 9 int ans; 10 11 void add(int v, int w) { 12 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 13 } 14 15 void dfs(int u, int pre, int d) { 16 if (d >= ans) return; 17 dist[u] = d; 18 for (int i = head[u]; i != -1; i = ne[i]) { 19 if (e[i] != pre && dist[e[i]] > d + 1) dfs(e[i], u, d + 1); 20 } 21 } 22 23 void solve() { 24 int n; 25 scanf("%d", &n); 26 for (int i = 0; i < n; i++) { 27 scanf("%d", c + i); 28 } 29 idx = 0; 30 memset(head, -1, sizeof(head)); 31 for (int i = 0; i < n - 1; i++) { 32 int v, w; 33 scanf("%d %d", &v, &w); 34 add(v, w), add(w, v); 35 } 36 ans = n + 1; 37 memset(dist, 0x3f, sizeof(dist)); 38 for (int i = 0; i < n; i++) { 39 ans = min(ans, dist[c[i]]); 40 if (i) printf("%d ", ans); 41 dfs(c[i], -1, 0); 42 } 43 printf("\n"); 44 } 45 46 int main() { 47 int t; 48 scanf("%d", &t); 49 while (t--) { 50 solve(); 51 } 52 53 return 0; 54 }
参考资料
Codeforces Round #847 (Div. 3) Editorial:https://codeforces.com/blog/entry/111948
Codeforces Round #847 (Div. 3) F(根号分治):https://zhuanlan.zhihu.com/p/601326343
标签:dist,int,text,Tree,sqrt,ans,Timofey,White,节点 From: https://www.cnblogs.com/onlyblues/p/17077001.html