[关闭]
@wsndy-xx 2018-05-05T11:24:57.000000Z 字数 3905 阅读 1242

Noip 2016 天天爱跑步解题报告

                                            wsndy
                                            18.05.04

题解

题面

小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。

这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 的连续正整数。

现在有 个玩家,第 个玩家的起点为 ,终点为 。每天打卡任务开始时,所有玩家在第 秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)

小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点 的观察员会选择在第 秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第 秒也理到达了结点 。小C想知道每个观察员会观察到多少人?

注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一段时间后再被观察员观察到。 即对于把结点 作为终点的玩家: 若他在第 秒前到达终点,则在结点 的观察员不能观察到该玩家;若他正好在第 秒到达终点,则在结点 的观察员可以观察到这个玩家。


题意

存在一棵树,树上的每个节点有一个观察时间,如果一条路径从起点到终点的过程中经过这个点的时间(从起点出发的时间为 , 每经过一条边花费时间为 )与这个点的观察时间相同,那么这个点就会观察到这条路径,询问 条路径后每个节点观察到的路径数量。


前置知识

    lca - 对路径划分
    树链剖分 - 求lca,化树成链
    树上差分 - 统计
    线段树 (动态开节点) - 统计

变量表达

    deep[a]  节点a的深度
    wat[a]   节点a出现观察员的时间 //watch
    s        一条路径的起点,代码中用Askl[]表示
    t        一条路径的终点,代码中用Askr[]表示
    lca(a,b) a,b 的lca

思考

对于树上的问题往往将树上的一段路径拆成 两条路径来处理

首先将路径拆成 两段路径
对于 是向上的路径,如果该路径能被 节点观察到,我们就可以得到


显然对与节点 ,它能观察到的向上跑的玩家,是所有的
的玩家
也就是说在以 为根的子树中(涉及链剖的思想)所有路径起点深度为 的路径都可以被节点 观察到
这里考虑树剖的第 就可以处理以 为根的子树,将以 为根的子树转化为一段区间
然后,对每一个深度都建立一颗线段树(空间不够,动态开点),那么就可以在深度为 的线段树中查询区间 被观察到的路径条数
注意线段树维护的是该深度下路径起点的个数,那么查询该深度中的某段区间,就相当于查询该深度下该区间中的路径的起点个数
如何标记观察到的路径条数??
考虑差分
,在 的父亲节点处
这里可以对照序列差分理解
这样就可以进行区间和的统计啦

同理,对于 的这段路径


这样在 深度为 的线段树中,终点处 ,注意这里是在 , 防止 处多加
查询时查询深度为 的线段树即可
由于 可能出现负数,所以要加一个数,使深度不为负数。


Code

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. using namespace std;
  5. const int N = 3e5 + 10;
  6. #define gc getchar()
  7. int n, m, wat[N];
  8. int Askl[N], Askr[N], Lca[N];
  9. int now = 1, head[N];
  10. struct Node {int v, nxt;} G[N << 1];
  11. inline int read() {
  12. int x = 0; char c = gc;
  13. while(c < '0' || c > '9') c = gc;
  14. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;
  15. return x;
  16. }
  17. inline void Add(int u, int v) {G[now].v = v; G[now].nxt = head[u]; head[u] = now ++;}
  18. int fa[N], deep[N], top[N], size[N], son[N], lst[N], rst[N], tree[N], Spjs;
  19. void Dfs_1(int u, int f_, int dep) {
  20. fa[u] = f_; deep[u] = dep; size[u] = 1;
  21. for(int i = head[u]; ~ i; i = G[i].nxt) {
  22. int v = G[i].v;
  23. if(v != f_) {
  24. Dfs_1(v, u, dep + 1);
  25. size[u] += size[v];
  26. if(size[son[u]] < size[v]) son[u] = v;
  27. }
  28. }
  29. }
  30. void Dfs_2(int u, int tp) {
  31. top[u] = tp;
  32. lst[u] = ++ Spjs;
  33. tree[u] = Spjs;
  34. if(! son[u]) {rst[u] = Spjs; return ;}
  35. Dfs_2(son[u], tp);
  36. for(int i = head[u]; ~ i; i = G[i].nxt) {
  37. int v = G[i].v;
  38. if(v != fa[u] && v != son[u]) Dfs_2(v, v);
  39. }
  40. rst[u] = Spjs;
  41. }
  42. inline int Ask_Lca(int x, int y) {
  43. int tp1 = top[x], tp2 = top[y];
  44. while(tp1 != tp2) {
  45. if(deep[tp1] < deep[tp2]) swap(x, y), swap(tp1, tp2);
  46. x = fa[tp1];
  47. tp1 = top[x];
  48. }
  49. return deep[x] < deep[y] ? x : y;
  50. }
  51. int root[N * 3], lson[N * 25], rson[N * 25], W[N * 25], tot, cnt;
  52. void Build_G(int l, int r, int & jd, int x, int yj) {
  53. if(!x) return ;
  54. if(!jd) jd = ++ tot;
  55. W[jd] += yj;
  56. if(l == r) return ;
  57. int mid = (l + r) >> 1;
  58. if(x <= mid) Build_G(l, mid, lson[jd], x, yj);
  59. else Build_G(mid + 1, r, rson[jd], x, yj);
  60. }
  61. int Sec_A(int jd, int l, int r, int x, int y) {
  62. if(! jd) return 0;
  63. if(x <= l && r <= y) return W[jd];
  64. int mid = (l + r) >> 1;
  65. if(y <= mid) return Sec_A(lson[jd], l, mid, x, y);
  66. else if(x > mid) return Sec_A(rson[jd], mid + 1, r, x, y);
  67. else return Sec_A(lson[jd], l, mid, x, y) + Sec_A(rson[jd], mid + 1, r, x, y);
  68. }
  69. void Clear() {
  70. tot = 0;
  71. memset(lson, 0, sizeof lson);
  72. memset(rson, 0, sizeof rson);
  73. memset(W, 0, sizeof W);
  74. memset(root, 0, sizeof root);
  75. }
  76. int Answer[N];
  77. int main() {
  78. n = read(); m = read();
  79. for(int i = 1; i <= n; i ++) head[i] = -1;
  80. for(int i = 1; i <= n - 1; i ++) {
  81. int u = read(), v = read();
  82. Add(u, v); Add(v, u);
  83. }
  84. for(int i = 1; i <= n; i ++) wat[i] = read();
  85. for(int i = 1; i <= m; i ++) Askl[i] = read(), Askr[i] = read();
  86. Dfs_1(1, 0, 0);
  87. Dfs_2(1, 1);
  88. for(int i = 1; i <= m; i ++) Lca[i] = Ask_Lca(Askl[i], Askr[i]);
  89. int dep;
  90. for(int i = 1; i <= m; i ++) {
  91. dep = deep[Askl[i]];
  92. Build_G(1, n, root[dep], tree[Askl[i]], 1);
  93. Build_G(1, n, root[dep], tree[fa[Lca[i]]], -1);
  94. }
  95. for(int i = 1; i <= n; i ++)
  96. Answer[i] = Sec_A(root[deep[i] + wat[i]], 1, n, lst[i], rst[i]);
  97. Clear();
  98. for(int i = 1; i <= m; i ++) {
  99. dep = deep[Askl[i]] - deep[Lca[i]] * 2 + n * 2;
  100. Build_G(1, n, root[dep], tree[Askr[i]], 1);
  101. Build_G(1, n, root[dep], tree[Lca[i]], -1);
  102. }
  103. for(int i = 1; i <= n; i ++)
  104. Answer[i] += Sec_A(root[wat[i] - deep[i] + n * 2], 1, n, lst[i], rst[i]);
  105. for(int i = 1; i <= n; i ++)
  106. cout << Answer[i] << " ";
  107. return 0;
  108. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注