• [lca][思维]Ancestor 2022牛客多校第3场 A


    题目描述

    NIO is playing a game about trees.

    The game has two trees A,BA, BA,B each with NNN vertices. The vertices in each tree are numbered from 111 to NNN and the iii-th vertex has the weight viv_ivi​. The root of each tree is vertex 1. Given KKK key numbers x1,…,xkx_1,\dots,x_kx1​,…,xk​, find the number of solutions that remove exactly one number so that the weight of the lowest common ancestor of the vertices in A with the remaining key numbers is greater than the weight of the lowest common ancestor of the vertices in B with the remaining key numbers.

    输入描述:

    The first line has two positive integers N,K(2≤K≤N≤105)N,K (2 \leq K \leq N \leq 10^5)N,K(2≤K≤N≤105).
    
    The second line has KKK unique positive integers x1,…,xK(xi≤N)x_1,\dots,x_K (x_i \leq N)x1​,…,xK​(xi​≤N).
    
    The third line has NNN positive integers ai(ai≤109)a_i (a_i \leq 10^9)ai​(ai​≤109) represents the weight of vertices in A.
    
    The fourth line has N−1N - 1N−1 positive integers {pai}\{pa_i\}{pai​}, indicating that the number of the father of vertices i+1i+1i+1 in tree A is paipa_ipai​.
    
    The fifth line has nnn positive integers bi(bi≤109)b_i (b_i \leq 10^9)bi​(bi​≤109) represents the weight of vertices in B.
    
    The sixth line has N−1N - 1N−1 positive integers {pbi}\{pb_i\}{pbi​}, indicating that the number of the father of vertices i+1i+1i+1 in tree B is pbipb_ipbi​.

    输出描述:

    One integer indicating the answer.

    示例1

    输入

    5 3
    5 4 3
    6 6 3 4 6
    1 2 2 4
    7 4 5 7 7
    1 1 3 2

    输出

    1

    说明

    In first case, the key numbers are 5,4,3. 
    Remove key number 5, the lowest common ancestors of the vertices in A with the remaining key numbers is 2, in B is 3.
    Remove key number 4, the lowest common ancestors of the vertices in A with the remaining key numbers is 2, in B is 1.
    Remove key number 3, the lowest common ancestors of the vertices in A with the remaining key numbers is 4, in B is 1.
    Only remove key number 5 satisfies the requirement.

    示例2

    输入

    10 3
    10 9 8
    8 9 9 2 7 9 0 0 7 4
    1 1 2 4 3 4 2 4 7
    7 7 2 3 4 5 6 1 5 3
    1 1 3 1 2 4 7 3 5

    输出

    2

    题意: 给出两颗树A和B,点的编号都是从1~n,每棵树上的点都有各自的价值wi,再给出一个包含k个点的关键点集合,现在可以移除关键点集合中的一个点,使得剩下关键点在A树上的LCA的点权大于在B树上的LCA的点权,求多少种方案符合要求。

    分析: 比赛的时候想到的是比较麻烦的模拟做法,根据树的形态来分类讨论,其实这样是非常麻烦的,赛后看到了正解,正解是维护前缀LCA和后缀LCA,这样就能很快求出剩下点的LCA了,时间复杂度为O(nlogn)。具体做法就是先建好两棵树,然后对于A树维护一个关键点的前缀LCA数组front[i][1],再维护一个后缀LCA数组back[i][1],对于B树同理得到front[i][2],back[i][2],然后就可以枚举移除哪个关键点了,剩下点在A树中的LCA就是lca(front[i-1][1], back[i+1][1]),在B树中的LCA就是lca(front[i-1][2], back[i+1][2]),注意边界的特判。

    具体代码如下: 

    正解:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. using namespace std;
    8. vector<int> g[100005][3];
    9. int n, k, w[100005][3], fa[100005][21][3], dep[100005][3], a[100005];
    10. int front[100005][3], back[100005][3];//前缀lca和后缀lca
    11. void dfs(int now, int pre, int type)
    12. {
    13. dep[now][type] = dep[pre][type]+1;
    14. fa[now][0][type] = pre;
    15. for(int i = 1; i <= 20; i++)//超出范围的祖先都是0号结点
    16. fa[now][i][type] = fa[fa[now][i-1][type]][i-1][type];
    17. for(int i = 0; i < g[now][type].size(); i++)
    18. if(g[now][type][i] != pre)
    19. dfs(g[now][type][i], now, type);
    20. }
    21. int lca(int x, int y, int type)
    22. {
    23. if(dep[x][type] < dep[y][type]) swap(x, y);
    24. for(int i = 20; i >= 0; i--)
    25. if(dep[fa[x][i][type]][type] >= dep[y][type])
    26. x = fa[x][i][type];
    27. if(x == y) return x;
    28. for(int i = 20; i >= 0; i--)
    29. if(fa[x][i][type] != fa[y][i][type])
    30. x = fa[x][i][type], y = fa[y][i][type];
    31. return fa[x][0][type];
    32. }
    33. signed main()
    34. {
    35. cin >> n >> k;
    36. for(int i = 1; i <= k; i++) scanf("%d", &a[i]);
    37. for(int i = 1; i <= n; i++) scanf("%d", &w[i][1]);
    38. for(int i = 2; i <= n; i++){
    39. int u;
    40. scanf("%d", &u);
    41. g[u][1].push_back(i);
    42. }
    43. for(int i = 1; i <= n; i++) scanf("%d", &w[i][2]);
    44. for(int i = 2; i <= n; i++){
    45. int u;
    46. scanf("%d", &u);
    47. g[u][2].push_back(i);
    48. }
    49. dfs(1, 0, 1);
    50. front[0][1] = a[1];
    51. back[k+1][1] = a[k];
    52. for(int i = 1; i <= k; i++) front[i][1] = lca(front[i-1][1], a[i], 1);
    53. for(int i = k; i >= 1; i--) back[i][1] = lca(back[i+1][1], a[i], 1);
    54. dfs(1, 0, 2);
    55. front[0][2] = a[1];
    56. back[k+1][2] = a[k];
    57. for(int i = 1; i <= k; i++) front[i][2] = lca(front[i-1][2], a[i], 2);
    58. for(int i = k; i >= 1; i--) back[i][2] = lca(back[i+1][2], a[i], 2);
    59. int ans = 0;
    60. for(int i = 1; i <= k; i++){//枚举移除哪个点
    61. int top1, top2;
    62. if(i < k && i > 1){
    63. top1 = lca(front[i-1][1], back[i+1][1], 1);
    64. top2 = lca(front[i-1][2], back[i+1][2], 2);
    65. }
    66. else if(i == 1){
    67. top1 = back[2][1];
    68. top2 = back[2][2];
    69. }
    70. else{
    71. top1 = front[k-1][1];
    72. top2 = front[k-1][2];
    73. }
    74. if(w[top1][1] > w[top2][2]) ans++;
    75. }
    76. printf("%d\n", ans);
    77. return 0;
    78. }

    比赛时的模拟做法:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #define inf 0x3f3f3f3f
    9. using namespace std;
    10. int a[100005], w[100005][3];
    11. int num[100005][3];
    12. bool flag[100005];
    13. int dep[100005][3], fa[100005][21][3];
    14. int F[100005][3];
    15. vector<int> tr[100005][3];
    16. int n, k;
    17. vector<int> temp;
    18. void dfs(int now, int pre, int type){
    19. dep[now][type] = dep[pre][type]+1;
    20. fa[now][0][type] = pre;
    21. for(int i = 1; i <= 20; i++)//超出范围的祖先都是0号结点
    22. fa[now][i][type] = fa[fa[now][i-1][type]][i-1][type];
    23. for(int i = 0; i < tr[now][type].size(); i++)
    24. if(tr[now][type][i] != pre)
    25. dfs(tr[now][type][i], now, type);
    26. }
    27. int lca(int x, int y, int type){
    28. if(dep[x][type] < dep[y][type]) swap(x, y);
    29. for(int i = 20; i >= 0; i--)
    30. if(dep[fa[x][i][type]][type] >= dep[y][type])
    31. x = fa[x][i][type];
    32. if(x == y) return x;
    33. for(int i = 20; i >= 0; i--)
    34. if(fa[x][i][type] != fa[y][i][type])
    35. x = fa[x][i][type], y = fa[y][i][type];
    36. return fa[x][0][type];
    37. }
    38. void dfs2(int now, int fa, int type, int super_fa){
    39. if(flag[now]){
    40. num[now][type]++;
    41. F[now][type] = super_fa;
    42. }
    43. for(int i = 0; i < tr[now][type].size(); i++){
    44. int to = tr[now][type][i];
    45. dfs2(to, now, type, super_fa);
    46. num[now][type] += num[to][type];
    47. }
    48. }
    49. void dfs3(int now, int type){
    50. if(flag[now]) temp.push_back(now);
    51. for(int i = 0; i < tr[now][type].size(); i++){
    52. int to = tr[now][type][i];
    53. dfs3(to, type);
    54. }
    55. }
    56. signed main()
    57. {
    58. scanf("%d%d", &n, &k);
    59. for(int i = 1; i <= k; i++){
    60. scanf("%d", &a[i]);
    61. flag[a[i]] = true;
    62. }
    63. for(int i = 1; i <= n; i++) scanf("%d", &w[i][1]);
    64. for(int i = 2; i <= n; i++){
    65. int to;
    66. scanf("%d", &to);
    67. tr[to][1].push_back(i);
    68. }
    69. for(int i = 1; i <= n; i++) scanf("%d", &w[i][2]);
    70. for(int i = 2; i <= n; i++){
    71. int to;
    72. scanf("%d", &to);
    73. tr[to][2].push_back(i);
    74. }
    75. dfs(1, 0, 1);
    76. dfs(1, 0, 2);
    77. int lca1 = a[1], lca2 = a[1];
    78. for(int i = 2; i <= k; i++){
    79. lca1 = lca(a[i], lca1, 1);
    80. lca2 = lca(a[i], lca2, 2);
    81. }
    82. int cnt1 = 0;
    83. for(int i = 0; i < tr[lca1][1].size(); i++){
    84. int to = tr[lca1][1][i];
    85. dfs2(to, lca1, 1, to);
    86. if(num[to][1]) cnt1++;
    87. }
    88. int cnt2 = 0;
    89. for(int i = 0; i < tr[lca2][2].size(); i++){
    90. int to = tr[lca2][2][i];
    91. dfs2(to, lca2, 2, to);
    92. if(num[to][2]) cnt2++;
    93. }
    94. int ans = 0;
    95. for(int i = 1; i <= k; i++){
    96. int t1 = -1, t2 = -1;
    97. if(cnt1 > 2) t1 = w[lca1][1];
    98. else if(a[i] == lca1){
    99. if(cnt1 == 2) t1 = w[lca1][1];
    100. else if(cnt1 == 1){
    101. temp.clear();
    102. for(int j = 1; j <= k; j++){
    103. if(a[j] == lca1) continue;
    104. temp.push_back(a[j]);
    105. }
    106. int top = temp[0];
    107. for(int j = 0; j < temp.size(); j++)
    108. top = lca(top, temp[j], 1);
    109. t1 = w[top][1];
    110. }
    111. //此时cnt1不可能为0
    112. }
    113. else if(cnt1 == 2){
    114. if(num[F[a[i]][1]][1] > 1) t1 = w[lca1][1];//lca不变
    115. else{
    116. temp.clear();
    117. for(int j = 1; j <= k; j++){
    118. if(a[j] == a[i]) continue;
    119. temp.push_back(a[j]);
    120. }
    121. int top = temp[0];
    122. for(int j = 0; j < temp.size(); j++)
    123. top = lca(top, temp[j], 1);
    124. t1 = w[top][1];
    125. }
    126. }
    127. else if(cnt1 == 1)
    128. t1 = w[lca1][1];
    129. if(cnt2 > 2) t2 = w[lca2][2];
    130. else if(a[i] == lca2){
    131. if(cnt2 == 2) t2 = w[lca2][2];
    132. else if(cnt2 == 1){
    133. temp.clear();
    134. for(int j = 1; j <= k; j++){
    135. if(a[j] == lca2) continue;
    136. temp.push_back(a[j]);
    137. }
    138. int top = temp[0];
    139. for(int j = 0; j < temp.size(); j++)
    140. top = lca(top, temp[j], 2);
    141. t2 = w[top][2];
    142. }
    143. //此时cnt1不可能为0
    144. }
    145. else if(cnt2 == 2){
    146. if(num[F[a[i]][2]][2] > 1) t2 = w[lca2][2];//lca不变
    147. else{
    148. temp.clear();
    149. for(int j = 1; j <= k; j++){
    150. if(a[j] == a[i]) continue;
    151. temp.push_back(a[j]);
    152. }
    153. int top = temp[0];
    154. for(int j = 0; j < temp.size(); j++)
    155. top = lca(top, temp[j], 2);
    156. t2 = w[top][2];
    157. }
    158. }
    159. else if(cnt2 == 1)
    160. t2 = w[lca2][2];
    161. if(t1 > t2) ans++;
    162. }
    163. printf("%d\n", ans);
    164. return 0;
    165. }

  • 相关阅读:
    Hash 的定义
    antlr4例子步骤
    国民技术Cortex-M0系列单片机IAP升级
    MyBatis 配置详解
    Unity中的AI算法和实现1-Waypoint
    FairMOT 论文学习
    数据好合: Argilla 和 Hugging Face Spaces 携手赋能社区合力构建更好的数据集
    实战项目如何抵御即跨站脚本(XSS)攻击
    迪文屏K600+ 数据库的读写操作
    面试突击32:为什么创建线程池一定要用ThreadPoolExecutor?
  • 原文地址:https://blog.csdn.net/m0_55982600/article/details/126008692