F. Timofey and Black-White Tree

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 c0, is colored white. The vertex c0 is colored black.

Timofey wants to color all the vertices of this tree in black. To do this, he performs n1 operations. During the i-th operation, he selects the vertex ci, 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 (1t104) — the number of testcases.

The first line of each testcase contains the integers n,c0 (2n2105, 1c0n) — the number of vertices in the tree and index of the initial black vertex.

The second line of each testcase contains n1 unique integers c1,c2,,cn1 (1cin, cic0), where ci is the vertex which is colored black during the i-th operation.

Each of the next n1 row of each testcase contains the integers vi,ui (1vi,uin) — edges in the tree.

It is guaranteed that the sum of n for all testcases does not exceed 2105.

Output

For each testcase, print n1 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(n2)。实际上给出的做法也是暴力的思路,不过在bfs的时候加了剪枝,最终的时间复杂度变成了O(nn)

  在下面讲述的方法中dist数组的定义与原先的定义不同,原先bfs中dist[v]表示从始点(树根)到节点v的最小值,下面的方法中dist[v]表示离节点v最近的已被染黑的点的距离。注意这里的最近不是指最新被染黑的节点,而是所有已经染黑的节点中,距离它最近的那个节点。

  大致的流程为每染黑一个点ci,把ci加入队列进行bfs。在bfs的过程中假设当前的节点为u,与u相邻的节点都记作v,只有dist[v]>dist[u]+1并且dist[u]+1<ans才更新dist[v]并把v加入队列,其中ans记录的是任意两个被染黑的点间的最小距离,显然ans只会变小不会变大。最后是答案的输出,每次遍历到ci,那么就对ansdist[ci]取最小值,再输出ans(先输出答案再染黑ci)。首先ans已经记录了之前所有被染黑的点间的最小距离,在染黑ci后本质上是想知道ci到其他已染黑的点最短距离,然后更新ans,而dist[ci]就表示所有已染黑的点中距离ci的最短距离,因此在染黑ci前取ans=min{ans,dist[ci]}就可以达到此目的。

  下面重点讲讲剪枝。首先是只有dist[v]>dist[u]+1才更新dist[v]。这是很显然的,问题就是如果v不加入队列那么以v为根的子树都得不到更新。实际上这些节点都不需要更新,因为这些节点之前都是通过dist[v]更新得到最小距离的,如果要以dist[u]+1更新距离,明显不如dist[v]小,因此这些节点是不会被更新的。

  然后是dist[u]+1<ans才更新dist[v]。首先补充一个概念,在长度为n的数轴中加入n个点,那么任意两点间的最小距离所能够取到的最大值为n(准确来说是n这个量级),即当所有点都按照间隔n来排列时取到。这个结论在树中也成立,在一颗n个节点的树中选择n个节点,所能取到的任意两个节点的最小距离的最大值为n量级。这个结论也是参考别人的说法,我感觉不是很显然,也许可以通过dfs序来证明,这里就默认这个结论是正确的。意味着当已经染黑了n个节点,此时的ans必然不超过n(量级)。

  因此按照上面的算法在枚举完前n个节点时的时间复杂度为O(nn),枚举剩下的节点的时间复杂度也是O(nn),因此总的时间复杂度就是O(nn)。下面来分析枚举剩下的节点的时间复杂度。bfs的时间复杂度取决于所有弹出队列的节点的度数,因此可以计算在枚举剩下的节点的过程中,所有节点能够入队的次数。因为枚举完前n个节点后,任意两个黑色节点的最短距离不超过n,而要入队的只有白色节点,在dist[u]+1<ans才更新dist[v]的限制下,可以发现每个节点最多只能入队n次,因此所有节点入队的总次数就是O(nn)的量级,因此时间复杂度也是O(nn)

  可以发现这总做法被分成了两个部分,前部分是枚举前n个节点,后部分是枚举剩下的节点,而这两部分的时间复杂度均为O(nn),这种做法被称为根号分治。

  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

posted @   onlyblues  阅读(118)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示