CodeForces 161D Distance in Tree 点分治

忘是亡心i 2023-05-21 07:23 120阅读 0赞

题目链接: http://codeforces.com/problemset/problem/161/D
题意:给你一棵树,让你求有多少对点的距离等于k 点分治裸题,每次分治子树记录经过当前根节点的距离

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. #define fi first
  5. #define se second
  6. #define ls rt << 1
  7. #define rs rt << 1|1
  8. #define po pop_back
  9. #define pb push_back
  10. #define mk make_pair
  11. #define lson l, mid, ls
  12. #define rson mid + 1, r, rs
  13. #define pll pair<ll, ll>
  14. #define pii pair<int, int>
  15. #define ull unsigned long long
  16. #define pdd pair<double, double>
  17. const int mod = 1e9 + 7;
  18. const int maxn = 5e4 + 10;
  19. const int inf = 0x3f3f3f3f;
  20. const ll linf = 0x3f3f3f3f3f3f3f3f;
  21. int head[maxn * 2], nxt[maxn * 2], to[maxn * 2], ver[maxn * 2], tot1 = -1;
  22. void add(int u, int v, int w)
  23. {
  24. nxt[++tot1] = head[u]; //当前边的后继
  25. head[u] = tot1; //起点u的第一条边
  26. to[tot1] = v; //当前边的终点
  27. ver[tot1] = w; //当前边的权值
  28. }
  29. vector<int> vc; //离线询问
  30. int sz[maxn], Ma[maxn], vis[maxn]; //sz子树大小 Ma[u]删除u的子树中最大的树大小
  31. int dis[maxn], q[maxn], id[maxn]; //q记录处理子树时出现过的距离值 id记录q方便清空res
  32. int rt, tot, R; //分治根 子树总结点数 记录长度
  33. ll res[1000010], ans[1000010]; //从分治点出现的路径长度 所有出现过的路径长度
  34. void getrt(int u, int fa) //得到子树的非带权重心
  35. {
  36. sz[u] = 1, Ma[u] = 0; //初始化
  37. for(int i = head[u]; ~i; i = nxt[i])
  38. {
  39. int v = to[i];
  40. if(vis[v] || v == fa) continue;
  41. getrt(v, u); //递归得到子树大小
  42. sz[u] += sz[v];
  43. Ma[u] = max(Ma[u], sz[v]); //更新u结点的Ma
  44. }
  45. Ma[u] = max(Ma[u], tot - sz[u]);
  46. if(Ma[u] < Ma[rt]) rt = u; //更新当前子树的重心
  47. }
  48. void dfs(int u, int fa)
  49. {
  50. q[++R] = dis[u]; //记录所有出现的距离值
  51. for(int i = head[u]; ~i; i = nxt[i])
  52. {
  53. int v = to[i], w = ver[i];
  54. if(vis[v] || v == fa) continue;
  55. dis[v] = dis[u] + w;
  56. //if(dis[v] > 10000000) continue;
  57. dfs(v, u); //可以进行剪枝例如大于1e7的剪掉
  58. }
  59. }
  60. void calc(int u)
  61. {
  62. int pos = 0;
  63. for(int i = head[u]; ~i; i = nxt[i])
  64. {
  65. int v = to[i], w = ver[i];
  66. if(vis[v]) continue;
  67. R = 0; //初始化
  68. dis[v] = w; //第一个点的距离
  69. dfs(v, u); //处理每个子树的距离
  70. for(int k = R; k; --k) //遍历当前子树的距离
  71. {
  72. for(int j = 0; j < vc.size(); ++j)
  73. if(vc[j] >= q[k]) //对于每个询问如果路径存在就标记询问
  74. ans[vc[j]] += res[vc[j] - q[k]];
  75. }
  76. for(int k = R; k; --k) //标记出现过的距离
  77. res[q[k]]++, id[++pos] = q[k];
  78. }
  79. for(int i = 1; i <= pos; ++i) //每处理完一个子树就清空
  80. res[id[i]] = 0;
  81. }
  82. void slove(int u)
  83. {
  84. vis[u] = 1, res[0] = 1; //res[i]表示到根距离为i的路径是否存在
  85. calc(u); //处理以u为根的子树
  86. for(int i = head[u]; ~i; i = nxt[i]) //对每个子树进行分治
  87. {
  88. int v = to[i];
  89. if(vis[v]) continue;
  90. tot = sz[v], Ma[rt = 0] = inf; //设置子树大小
  91. getrt(v, 0); //找到新的rt
  92. slove(rt);
  93. }
  94. }
  95. int main()
  96. {
  97. memset(head, -1, sizeof(head));
  98. int n, k;
  99. scanf("%d%d", &n, &k);
  100. for(int i = 1; i < n; ++i)
  101. {
  102. int u, v, w = 1;
  103. scanf("%d%d", &u, &v);
  104. add(u, v, w), add(v, u, w);
  105. }
  106. vc.pb(k);
  107. tot = Ma[rt] = n; //首次先找整棵树的重心
  108. getrt(1, 0);
  109. slove(rt);
  110. printf("%lld\n", ans[k]);
  111. return 0;
  112. }

发表评论

表情:
评论列表 (有 0 条评论,120人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Codeforces 1101D 分治

    题意:有一颗树,每个点有一个点权,边权都是1,问路径上的所有点的gcd不是1的最长路径是多少? 思路:之前补这道题的时候,用分解质因数 + 树形DP做的,其实用点分治可以更暴