另一道树题

题目大意

数据范围


题解

这个题第一眼能发现的是,我们的答案分成两种情况。

第一种是在非根节点汇合,第二种是在根节点汇合。

尝试枚举在第几回合结束,假设在第$i$回合结束的方案数为$f_i$,那么总答案就是$\sum\limits_{i = 1} ^ {N - 1}i\times f_i$。

显然没法求这个$f_i$....

进而,觉得这鬼东西的后缀和好像比较好求,就是$g _ i = \sum\limits_{j = i} ^ {N - 1} f _ j$。

由于我们就相当于对于深度相等的点的讨论,不难想到$bfs$序。

只考虑不在根节点汇合的情况。

发现,其实就是一段连续的区间,他们在$i$不小于一个值的时候,最多只能选取一个值。

也就是说随着我们枚举的回合数递增,这些连续的区间会存在一些合并的情况。

至于什么时候合并呢?其实就根据,相邻两个点到其$lca$的深度有关(这两个点的深度得相等),就是在这个深度差恰好等于回合数的时候,我们实施合并操作。

这样就完美的解决了不是非根汇合的情况。

考虑在根节点汇合咋办。

其实就相当于,随着回合数递增,所有深度不大于$i$的点只能选一个,就相当于和根节点合并咯。

总之通通用并查集维护就好了。

代码

  1. #include <bits/stdc++.h>
  2.  
  3. #define N 200010
  4.  
  5. using namespace std;
  6.  
  7. int head[N], to[N << 1], nxt[N << 1], tot;
  8.  
  9. struct Node {
  10. int x, y;
  11. };
  12.  
  13. vector <Node> v[N];
  14.  
  15. queue <int> q;
  16.  
  17. int f[20][N], g[N], F[N], S[N], dep[N], dic[N], n, inv[N];
  18.  
  19. const int mod = 998244353 ;
  20.  
  21. typedef long long ll;
  22.  
  23. char *p1, *p2, buf[100000];
  24.  
  25. #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ )
  26.  
  27. int rd() {
  28. int x = 0, f = 1;
  29. char c = nc();
  30. while (c < 48) {
  31. if (c == '-')
  32. f = -1;
  33. c = nc();
  34. }
  35. while (c > 47) {
  36. x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
  37. }
  38. return x * f;
  39. }
  40.  
  41. int qpow(int x, int y) {
  42. int ans = 1;
  43. while (y) {
  44. if (y & 1) {
  45. ans = (ll)ans * x % mod;
  46. }
  47. y >>= 1;
  48. x = (ll)x * x % mod;
  49. }
  50. return ans;
  51. }
  52.  
  53. inline void add(int x, int y) {
  54. to[ ++ tot] = y;
  55. nxt[tot] = head[x];
  56. head[x] = tot;
  57. }
  58.  
  59. int lca(int x, int y) {
  60. if (dep[x] < dep[y])
  61. swap(x, y);
  62. for (int i = 19; ~i; i -- ) {
  63. if (dep[f[i][x]] >= dep[y]) {
  64. x = f[i][x];
  65. }
  66. }
  67. if (x == y)
  68. return x;
  69. for (int i = 19; ~i; i -- ) {
  70. if (f[i][x] != f[i][y]) {
  71. x = f[i][x];
  72. y = f[i][y];
  73. }
  74. }
  75. return f[0][x];
  76. }
  77.  
  78. void dfs(int p, int fa) {
  79. v[dep[p]].push_back((Node){1, p});
  80. f[0][p] = fa;
  81. for (int i = 1; i <= 19; i ++ ) {
  82. f[i][p] = f[i - 1][f[i - 1][p]];
  83. }
  84. for (int i = head[p]; i; i = nxt[i]) {
  85. if (to[i] != fa) {
  86. dep[to[i]] = dep[p] + 1;
  87. dfs(to[i], p);
  88. }
  89. }
  90. }
  91.  
  92. void bfs() {
  93. while (!q.empty())
  94. q.pop();
  95. q.push(1);
  96. int cnt = 0;
  97. while (!q.empty()) {
  98. int x = q.front();
  99. q.pop();
  100. dic[ ++ cnt] = x;
  101. for (int i = head[x]; i; i = nxt[i]) {
  102. if (to[i] != f[0][x]) {
  103. q.push(to[i]);
  104. }
  105. }
  106. }
  107. for (int i = 1; i < n; i ++ ) {
  108. if (dep[dic[i]] == dep[dic[i + 1]]) {
  109. v[dep[dic[i]] - dep[lca(dic[i], dic[i + 1])]].push_back((Node) {dic[i], dic[i + 1]});
  110. }
  111. }
  112. }
  113.  
  114. int find(int x) {
  115. return F[x] == x ? x : F[x] = find(F[x]);
  116. }
  117.  
  118. int main() {
  119. n = rd();
  120. for (int i = 1; i <= n; i ++ ) {
  121. F[i] = i;
  122. S[i] = 1;
  123. }
  124. for (int i = 2; i <= n; i ++ ) {
  125. int x = rd();
  126. add(x, i);
  127. add(i, x);
  128. }
  129. dfs(1, 1);
  130. bfs();
  131. inv[0] = 1;
  132. for (int i = 1; i <= n; i ++ )
  133. inv[i] = qpow(i, mod - 2);
  134.  
  135. // for (int i = 0 ; i <= n; i ++ ) {
  136. // printf("%d ", inv[i]);
  137. // }
  138. // puts("");
  139.  
  140. int mdl = qpow(2, n);
  141. for (int i = 1; i < n; i ++ ) {
  142. g[i] = (mdl - n - 1 + mod) % mod;
  143. int len = v[i].size();
  144. for (int j = 0; j < len; j ++ ) {
  145. int x = v[i][j].x, y = v[i][j].y;
  146. x = find(x), y = find(y);
  147. if (x != y) {
  148. mdl = (ll)mdl * inv[S[x] + 1] % mod * inv[S[y] + 1] % mod;
  149. F[x] = y; S[y] += S[x];
  150. mdl = (ll)mdl * (S[y] + 1) % mod;
  151. }
  152. }
  153. }
  154. int ans = 0;
  155. for (int i = 1; i < n; i ++ ) {
  156. ans = (ans + (ll)(g[i] - g[i + 1] + mod) % mod * i % mod) % mod;
  157. }
  158. cout << ans << endl ;
  159. return 0;
  160. }

小结:好题好题,这个题的思路行云流水。重点是能否想到把那个,一段区间只能选一个这个事情考虑清楚,从而转变成区间的合并问题,这是关键。

[Comet OJ - Contest #6 D][48D 2280]另一道树题_并查集的更多相关文章

  1. Comet OJ - Contest #2 简要题解

    Comet OJ - Contest #2 简要题解 cometoj A 模拟,复杂度是对数级的. code B 易知\(p\in[l,r]\),且最终的利润关于\(p\)的表达式为\(\frac{( ...

  2. Comet OJ - Contest #2简要题解

    Comet OJ - Contest #2简要题解 前言: 我没有小裙子,我太菜了. A 因自过去而至的残响起舞 https://www.cometoj.com/contest/37/problem/ ...

  3. Comet OJ - Contest #4--前缀和

    原题:Comet OJ - Contest #4-B https://www.cometoj.com/contest/39/problem/B?problem_id=1577传送门 一开始就想着暴力打 ...

  4. Comet OJ - Contest #11 题解&赛后总结

    Solution of Comet OJ - Contest #11 A.eon -Problem designed by Starria- 在模 10 意义下,答案变为最大数的最低位(即原数数位的最 ...

  5. Comet OJ - Contest #8

    Comet OJ - Contest #8 传送门 A.杀手皇后 签到. Code #include <bits/stdc++.h> using namespace std; typede ...

  6. Comet OJ - Contest #13-C2

    Comet OJ - Contest #13-C2 C2-佛御石之钵 -不碎的意志-」(困难版) 又是一道并查集.最近做过的并查集的题貌似蛮多的. 思路 首先考虑,每次处理矩形只考虑从0变成1的点.这 ...

  7. Comet OJ - Contest #13 「火鼠的皮衣 -不焦躁的内心-」

    来源:Comet OJ - Contest #13 芝士相关: 复平面在信息学奥赛中的应用[雾 其实是道 sb 题??? 发现原式貌似十分可二项式定理,然后发现确实如此 我们把 \(a^i\) 替换成 ...

  8. Comet OJ - Contest #13 「佛御石之钵 -不碎的意志-」(hard)

    来源:Comet OJ - Contest #13 一眼并查集,然后发现这题 tmd 要卡常数的说卧槽... 发现这里又要用并查集跳过访问点,又要用并查集维护联通块,于是开俩并查集分别维护就好了 一开 ...

  9. Comet OJ - Contest #5

    Comet OJ - Contest #5 总有一天,我会拿掉给\(dyj\)的小裙子的. A 显然 \(ans = min(cnt_1/3,cnt_4/2,cnt5)\) B 我们可以感性理解一下, ...

随机推荐

  1. hierarchyviewer

    支持的版本更低

  2. IntelliJ IDEA 创建 Git 分支并且 Push 到远程

    在 IntelliJ 的右下角,你可以看到当前的 Git 分支,然后你可以单击这个分支后,在弹出的界面的最上方有一个新建分支的选项. 然后再弹出的界面中,输入你要创建的分支名称后回车输入. 然后从项目 ...

  3. jQuery系列(八):jQuery的位置信息

    1.宽度和高度 (1):获取宽度 .width() 描述:为匹配的元素集合中获取第一个元素的当前计算宽度值.这个方法不接受任何参数..css(width) 和 .width()之间的区别是后者返回一个 ...

  4. 【线性代数】2-1:解方程组(Ax=b)

    title: [线性代数]2-1:解方程组(Ax=b) toc: true categories: Mathematic Linear Algebra date: 2017-08-31 15:08:3 ...

  5. 求二叉树的层次遍历(SDUT 2824)

    Problem Description 已知一颗二叉树的前序遍历和中序遍历,求二叉树的层次遍历. Input 输入数据有多组,输入T,代表有T组测试数据.每组数据有两个长度小于50的字符串,第一个字符 ...

  6. umei-spider

    umei-spider 1 #!/usr/bin/python3 2 3 import requests 4 from bs4 import BeautifulSoup 5 from contextl ...

  7. 2017 ZSTU寒假排位赛 #7

    题目链接:https://vjudge.net/contest/149498#overview. A题,水题,直接按照题意模拟一下即可. B题,我用的是线段树.大力用的差分标记(上次听zy说过,下次再 ...

  8. CF981D

    CF981D 题意: 给你n个数,要求你分成k堆.每堆的内部加和,每堆之间是相与.问最大的值. 解法: 二进制下最大的数的所有位一定是1,所以贪心去找是否最大一定是正确的. 然后DP记录+贪心就可以A ...

  9. rabbitmq权限细分二

    禁止用户远程登录 主要有以下几步 1.进入容器 docker exec -it ID /bin/bash 2.打开配置文件 vi /etc/rabbitmq/rabbitmq.conf 3.加入配置, ...

  10. 3.JSON使用

    把 JSON 文本转换为 JavaScript 对象 JSON 最常见的用法之一,是从 web 服务器上读取 JSON 数据(作为文件或作为 HttpRequest),将 JSON 数据转换为 Jav ...