题意

LOJ #2359. 「NOIP2016」天天爱跑步

题解

考虑把一个玩家的路径 \((x, y)\) 拆成两条,一条是 \(x\) 到 \(lca\) ( \(x, y\) 最近公共祖先) 的路径,另一条是 \(lca\) 到 \(y\) 的路径。(对于 \(x, y\) 是 \(lca\) 的情况需要特殊考虑一下就行了)

这个求 \(lca\) 的过程用倍增实现就行了。

假设令到达时间为 \(at\) 。

不难发现,在树上向上的路径满足 \(dep_u + at_u=d_1\) (深度+到达时间) 是个定值。这个可以这样考虑,向上走 到达时间 \(+1\) ,且深度会 \(-1\) ,所以不会变。

同理可得,向下走的路径满足 \(dep_u - at_u=d_2\) (深度-到达时间) 是个定值。

我们考虑对于一条路径,差分表示在树上,也就是 \(x \to y\) 这条路径,我们在 \(x\) 处加入, \(y\) 处除去。

然后考虑每次我们线段树合并两个子树维护关于 \(d_1\) 以及 \(d_2\) 出现的次数。

然后对于一个点 \(u\) 要查询的就是 \(dep_u + w_u = d_1'\) 的值,以及 \(dep_u - w_u = d_2'\) 的值。

时间复杂度是 \(O(n \log n)\) 的,其实跑得挺快的?

具体看代码实现吧qwq。

代码

  1. #include <bits/stdc++.h>
  2. #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
  3. #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
  4. #define Set(a, v) memset(a, v, sizeof(a))
  5. #define Cpy(a, b) memcpy(a, b, sizeof(a))
  6. #define debug(x) cout << #x << ": " << x << endl
  7. #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
  8. using namespace std;
  9. inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
  10. inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
  11. inline int read() {
  12. int x = 0, fh = 1; char ch = getchar();
  13. for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
  14. for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
  15. return x * fh;
  16. }
  17. void File() {
  18. #ifdef zjp_shadow
  19. freopen ("2359.in", "r", stdin);
  20. freopen ("2359.out", "w", stdout);
  21. #endif
  22. }
  23. const int N = 3e5 + 1e3, Maxn = N * 40;
  24. #define lson ls[o], l, mid
  25. #define rson rs[o], mid + 1, r
  26. struct Segment_Tree {
  27. int ls[Maxn], rs[Maxn], sumv[Maxn], Size;
  28. Segment_Tree() { Size = 0; }
  29. void Update(int &o, int l, int r, int up, int uv) {
  30. if (!o) o = ++ Size;
  31. if (l == r) { sumv[o] += uv; return ; }
  32. int mid = (l + r) >> 1;
  33. if (up <= mid) Update(lson, up, uv); else Update(rson, up, uv);
  34. }
  35. int Query(int o, int l, int r, int qp) {
  36. if (l == r) return sumv[o];
  37. int mid = (l + r) >> 1;
  38. return qp <= mid ? Query(lson, qp) : Query(rson, qp);
  39. }
  40. int Merge(int x, int y, int l, int r) {
  41. if (!x || !y) return x | y;
  42. if (l == r) { sumv[x] += sumv[y]; return x; }
  43. int mid = (l + r) >> 1;
  44. ls[x] = Merge(ls[x], ls[y], l, mid);
  45. rs[x] = Merge(rs[x], rs[y], mid + 1, r);
  46. return x;
  47. }
  48. } TU, TD;
  49. int to[N][23], dep[N], Log2[N]; vector<int> G[N];
  50. void Dfs_Init(int u, int fa = 0) {
  51. to[u][0] = fa; dep[u] = dep[fa] + 1;
  52. for (int v : G[u]) if (v != fa) Dfs_Init(v, u);
  53. }
  54. int tmp;
  55. inline int Get_Lca(int x, int y) {
  56. if (dep[x] < dep[y]) swap(x, y);
  57. int gap = dep[x] - dep[y];
  58. For (i, 0, Log2[gap] + 1)
  59. if ((gap >> i) & 1) x = to[x][i];
  60. if (x == y) return x;
  61. Fordown (i, Log2[dep[x]], 0)
  62. if (to[x][i] != to[y][i]) x = to[x][i], y = to[y][i]; tmp = y;
  63. return to[x][0];
  64. }
  65. int n, m, W[N], ans[N];
  66. vector<int> TagU[N], TagD[N], DelU[N], DelD[N];
  67. int rtU[N], rtD[N];
  68. void Dfs(int u, int fa = 0) {
  69. for (int v : G[u]) if (v ^ fa) {
  70. Dfs(v, u);
  71. rtU[u] = TU.Merge(rtU[u], rtU[v], -n, n * 2);
  72. rtD[u] = TD.Merge(rtD[u], rtD[v], -n, n * 2);
  73. }
  74. for (int pos : TagU[u]) TU.Update(rtU[u], -n, n * 2, pos, 1);
  75. for (int pos : TagD[u]) TD.Update(rtD[u], -n, n * 2, pos, 1);
  76. ans[u] = TU.Query(rtU[u], -n, n * 2, W[u] + dep[u]) +
  77. TD.Query(rtD[u], -n, n * 2, W[u] - dep[u]) ;
  78. for (int pos : DelU[u]) TU.Update(rtU[u], -n, n * 2, pos, -1);
  79. for (int pos : DelD[u]) TD.Update(rtD[u], -n, n * 2, pos, -1);
  80. }
  81. int main () {
  82. File();
  83. n = read(); m = read();
  84. For (i, 1, n - 1) { int u = read(), v = read(); G[u].push_back(v); G[v].push_back(u); }
  85. Dfs_Init(1);
  86. For (i, 2, n)
  87. Log2[i] = Log2[i >> 1] + 1;
  88. For (j, 1, Log2[n]) For (i, 1, n)
  89. to[i][j] = to[to[i][j - 1]][j - 1];
  90. For (i, 1, n)
  91. W[i] = read();
  92. For (i, 1, m) {
  93. int x = read(), y = read(), Lca = Get_Lca(x, y);
  94. int d1 = dep[x], d2 = - dep[x];
  95. if (Lca == y) { TagU[x].push_back(d1); DelU[y].push_back(d1); continue ; }
  96. if (Lca == x) { TagD[y].push_back(d2); DelD[x].push_back(d2); continue ; }
  97. d2 = (dep[x] - dep[Lca] + 1) - dep[tmp];
  98. TagU[x].push_back(d1); DelU[Lca].push_back(d1);
  99. TagD[y].push_back(d2); DelD[tmp].push_back(d2);
  100. }
  101. Dfs(1);
  102. For (i, 1, n)
  103. printf ("%d%c", ans[i], i == iend ? '\n' : ' ');
  104. return 0;
  105. }

LOJ #2359. 「NOIP2016」天天爱跑步(倍增+线段树合并)的更多相关文章

  1. 「NOIP2016」天天爱跑步 题解

    (声明:图片来源于网络) 「NOIP2016」天天爱跑步 题解 题目TP门 题目 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...

  2. [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]

    [NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...

  3. 洛谷P1600 天天爱跑步(线段树合并)

    小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn ...

  4. LOJ2359. 「NOIP2016」天天爱跑步【树上差分】

    LINK 思路 首先发现如果对于一个节点,假设一个节点需要统计从字数内来的贡献 需要满足\(dep_u - dep_s = w_u\) 这个条件其实可以转化成\(dep_u - w_u = dep_s ...

  5. 「NOIP2016」天天爱跑步

    传送门 Luogu 解题思路 树上差分+桶计数. 我们发现在一条路径上的点 \(i\) ,它可以观测到玩家的条件是: \(i \in (u \to LCA),dep_u=w_i+dep_i\) \(i ...

  6. BZOJ4719 NOIP2016天天爱跑步(线段树合并)

    线段树合并的话这个noip最难题就是个裸题了. 注意merge最后return x,以及如果需要区间查询的话这里还需要up,无数次死于这里. #include<iostream> #inc ...

  7. 「BZOJ2733」「洛谷3224」「HNOI2012」永无乡【线段树合并】

    题目链接 [洛谷] 题解 很明显是要用线段树合并的. 对于当前的每一个连通块都建立一个权值线段树. 权值线段树处理操作中的\(k\)大的问题. 如果需要合并,那么就线段树暴力合并,时间复杂度是\(nl ...

  8. LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】

    LINK 思路 首先暴力\(n^2\)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小 ...

  9. NOIP2016 天天爱跑步(线段树/桶)

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.天天爱跑步是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 N个结点 ...

随机推荐

  1. ES5中文分词(IK)

    ElasticSearch5中文分词(IK) ElasticSearch安装 官网:https://www.elastic.co 1.ElasticSearch安装 1.1.下载安装公共密钥 rpm ...

  2. Linux 环境变量梳理

    Linux中的环境变量有两种:全局变量和局部变量: 定义.访问.删除局部变量 查看全局变量 可以使用printenv或者env命令来打印所有的全局变量. 访问某一项全局变量,可以使用printenv ...

  3. 开发工具之Sublime编辑器

    sublime是一款轻量级的编辑器,可以从官网上进行下载最新版本.它有很多使用并且强大的功能支持.例如:GOTO,package 等快捷操作.但有时候下载的版本不能进行安装package contro ...

  4. 剑指offer(6)

    题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...

  5. Python 中关于 round 函数的小坑

    参考: http://www.runoob.com/w3cnote/python-round-func-note.html

  6. java学习之—并归排序

    /** * 并归排序 * Create by Administrator * 2018/6/26 0026 * 下午 5:13 **/ public class DArray { private lo ...

  7. 为linux主机增加file description

    在benchmarked写的服务器的时候就遇到了too many file open 这个报错. 由于遇到过很多次了,所以知道应该是单机fd打满了. 首先来看看 机器最多支持多少fd cat /pro ...

  8. Docker入门与实践

      一.Docker介绍 docker官网:https://www.docker.com/ Docker hub地址: https://hub.docker.com/   1.基本概念 Docker ...

  9. 如何抓取电商的数据 & Python

    如何抓取电商的数据 & Python https://www.zhihu.com/question/40720286 https://www.zhihu.com/question/382455 ...

  10. 不幸,我的Ryzen 7 1700X中招了,也有segfault

    在历经了I7-5775C,I7-5820K之后,决定尝鲜用一下为AMD漂亮翻身的Ryzen 7,海淘了一颗Ryzen 7 1700X 最近听说在极重负载的情况下,CPU会出错,于是从网上找来Kill- ...