~~~题面~~~

题解:

很久以前就想写了,一直没敢做,,,不过今天写完没怎么调就过了还是很开心的。

首先我们观察到跑步的人数是很多的,要一条一条的遍历显然是无法承受的,因此我们要考虑更加优美的方法。

首先我们假设观察者没有时间的限制,一天到晚都在观察。那么我们可以想到一个很显然的做法——差分。这样就可以很方便的求出一个点被经过了多少次。

貌似这样再加想一下就可以直接用树链剖分+差分搞了。

但是还有更好的算法,是O(n)的。

而且还是比较好写的。

首先我们观察可以被统计到的点要符合一个什么条件。

设点j上的观察者在w[j]的时候观察,那么也就是说从s出发,要刚好经过w[j]到达点j,才可以被点j上的观察者统计到。

由此我们可以列出两个式子:

1,当s在j的子树内,而t在j的上方时。

  $w[j] + dep[j] = dep[s_i]$

2,当t在j的子树内,而s在j的上方时

  $dis[i] - (dep[t_i] - dep[j]) = w[j]$ 其中dis[i]表示s ---> t的路程长度

  因为dis[i] - (dep[t_i] - dep[j])其实就是s ---> j的路径长,所以这个式子就显然成立了。

  如果我们将带i的放在左边,带j的放在右边,那么将会有

  $dis[i] - dep[t_i] = w[j] - dep[j]$

那s和t都在j的子树内怎么办?(都在上方显然不会统计到)

  现在这时如果我们还是套用上面的式子,那么会有两种情况

  (1),j是(s,t)的LCA,那么此时两个式子一旦其中之一被满足,另外一个就一定会被满足(因为两个式子的实质是一样的),那么s和t将分别对j产生一次贡献(否则没有贡献)

  (2),j不是(s,t)的LCA,那么此时两个式子可能会被满足,s和t不一定会对j产生贡献。但是这种情况下,不论式子是否被满足,s和t都是不应该对j产生贡献的。
  那么我们怎么避免这种情况?

    首先我们注意到一旦这种情况出现,LCA[s,t]将会是s和t最后一次可能产生贡献的地方,因此我们要在LCA[s,t]处,消除s和t的影响,使得s和t无法对上面的点产生贡献。

那么我们应该如何统计呢?

  注意到所有的式子都可以被表示为左边只有关于i的,右边只有关于j的情况,因此我们完全可以将式子的左边和右边单独计算。即分别用两个桶统计关于两个式子的满足情况。

比如说我们定义int bu[600100];  然后每次遇到一个$s_i$的时候,我们就令$dep[s_i]++$。然后我们在每进入一个点时,都记录一个tmp = bu[w[j] + dep[j]],那么桶内对应位置元素的个数就代表满足

$dep[s_i] = t$(t为桶中位置)的元素个数。因此我们访问bu[w[j] + dep[j]]就可以获取满足 $w[j] + dep[j] = dep[s_i]$ 的元素个数,而之所以要在进入之前先记录一下位置,则是为了准确获取子树内的贡献,保证不被之前的东西干扰。

  对第二个式子的处理方式也是类似的,只不过因为第二个式子中出现了减号,而且减号旁边大小关系不确定,因此我们需要的桶中位置可能为负,所以我们统一加上一个较大的数,将本来要占据负数的数组整体向后移位就可以了。

具体实现看代码。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define R register int
  4. #define AC 501000
  5. #define ac 910000//要开这么大。。。
  6. #define getchar() *o++
  7. char READ[], *o = READ;
  8. int n, m;
  9. int w[AC], ans[AC], s[AC], t[AC], dis[AC], dep[AC], may[ac], id[ac];
  10. int Head[AC], Next[ac], date[ac], tot;
  11. struct edge{
  12. int Head[AC], Next[ac], date[ac], tot;
  13. inline void add(int f, int w)
  14. {
  15. date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
  16. date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
  17. }
  18. }E1,E2,E3;//询问的边(LCA),查询的边(ans)
  19.  
  20. inline int read()
  21. {
  22. int x = ; char c = getchar();
  23. while(c > '' || c < '') c = getchar();
  24. while(c >= '' && c <= '') x = x * + c - '', c = getchar();
  25. return x;
  26. }
  27.  
  28. inline void add(int f, int w)
  29. {
  30. date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
  31. date[++tot] = f, Next[tot] = Head[w], Head[w] = tot;
  32. }
  33.  
  34. inline void add1(int f, int w, int S)
  35. {
  36. E3.date[++tot] = w, E3.Next[tot] = E3.Head[f], E3.Head[f] = tot, may[tot] = S;
  37. }
  38.  
  39. inline void add2(int f, int w)//这里只能连单向边
  40. {
  41. E2.date[++tot] = w, E2.Next[tot] = E2.Head[f], E2.Head[f] = tot;
  42. }
  43.  
  44. void pre()
  45. {
  46. int a, b;
  47. n = read(), m = read();
  48. for(R i = ; i < n; i++)
  49. {
  50. a = read(), b = read();
  51. add(a, b);
  52. }
  53. for(R i = ; i <= n; i++) w[i] = read();
  54. E1.tot = E2.tot = tot = ;
  55. for(R i = ; i <= m; i++)
  56. {
  57. s[i] = read(), t[i] = read();
  58. E1.add(s[i], t[i]);
  59. add1(s[i], i, );//标记为开始节点
  60. add1(t[i], i, );//标记为结束节点
  61. id[E1.tot - ] = id[E1.tot] = i;
  62. }
  63. }
  64.  
  65. struct get_LCA{
  66. int father[AC], LCA[ac]; bool z[AC];
  67.  
  68. inline int find(int x)
  69. {
  70. return (x == father[x]) ? x : father[x] = find(father[x]);
  71. }
  72.  
  73. void dfs(int x)
  74. {
  75. int now;
  76. z[x] = true;
  77. for(R i = Head[x]; i; i = Next[i])
  78. {
  79. now = date[i];
  80. if(z[now]) continue;
  81. dep[now] = dep[x] + ;
  82. dfs(now);
  83. father[now] = x;//访问完就要改父亲了
  84. }
  85. for(R i = E1.Head[x]; i; i = E1.Next[i])
  86. {
  87. now = E1.date[i];
  88. if(z[now] && !LCA[i ^ ])
  89. {
  90. // printf("%d %d\n", x, now);
  91. LCA[i] = find(now);
  92. dis[id[i]] = dep[x] - dep[LCA[i]] + dep[now] - dep[LCA[i]];
  93. add2(LCA[i], id[i]);//将LCA和询问联系起来
  94. }
  95. }
  96. }
  97.  
  98. void getLCA()
  99. {
  100. for(R i = ; i <= n; i++) father[i] = i;
  101. dep[] = ;
  102. dfs();
  103. }
  104. }LCA;
  105.  
  106. #define k 600000
  107. struct difference{
  108. int bu[ac], b[ac * ];
  109. void dfs(int x, int fa)
  110. {
  111. int tmp = bu[dep[x] + w[x]], rnt = b[w[x] - dep[x] + k], now;
  112. for(R i = E3.Head[x]; i; i = E3.Next[i])//加上当前节点的贡献
  113. {
  114. now = E3.date[i];
  115. if(!may[i]) ++bu[dep[x]];//如果是开始节点
  116. else ++b[dis[now] - dep[x] + k];//等式两边同时+k
  117. }
  118. for(R i = Head[x]; i; i = Next[i])//遍历子树
  119. {
  120. now = date[i];
  121. if(now == fa) continue;
  122. dfs(now, x);
  123. }
  124. ans[x] = bu[dep[x] + w[x]] - tmp + b[w[x] - dep[x] + k] - rnt;
  125. for(R i = E2.Head[x]; i; i = E2.Next[i])//查看当前节点是哪些点对的LCA
  126. {
  127. now = E2.date[i];
  128. if(dep[s[now]] == dep[x] + w[x]) --ans[x];//如果造成了贡献,那么必定是双倍,因此要减去
  129. --bu[dep[s[now]]];//减去贡献
  130. --b[dis[now] - dep[t[now]] + k];//等式两边同时+k!
  131. }
  132. }
  133.  
  134. }get;
  135.  
  136. void work()
  137. {
  138. for(R i = ; i <= n; i++) printf("%d ", ans[i]);
  139. printf("\n");
  140. }
  141.  
  142. int main()
  143. {
  144. freopen("in.in", "r", stdin);
  145. fread(READ, , , stdin);
  146. pre();
  147. LCA.getLCA();
  148. get.dfs(, );
  149. work();
  150. fclose(stdin);
  151. return ;
  152. }

[NOIP2016] 天天爱跑步 桶 + DFS的更多相关文章

  1. [Noip2016]天天爱跑步 LCA+DFS

    [Noip2016]天天爱跑步 Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要玩家每天按时上线,完成打卡任 ...

  2. luogu1600 [NOIp2016]天天爱跑步 (tarjanLca+dfs)

    经过部分分的提示,我们可以把一条路径切成s到lca 和lca到t的链 这样就分为向上的链和向下的链,我们分开考虑: 向上:如果某一个链i可以对点x产生贡献,那么有deep[x]+w[x]=deep[S ...

  3. [NOIp2016]天天爱跑步 线段树合并

    [NOIp2016]天天爱跑步 LG传送门 作为一道被毒瘤出题人们玩坏了的NOIp经典题,我们先不看毒瘤的"动态爱跑步"和"天天爱仙人掌",回归一下本来的味道. ...

  4. 【LG1600】[NOIP2016]天天爱跑步

    [LG1600][NOIP2016]天天爱跑步 题面 洛谷 题解 考虑一条路径\(S\rightarrow T\)是如何给一个观测点\(x\)造成贡献的, 一种是从\(x\)的子树内出来,另外一种是从 ...

  5. NOIP2016天天爱跑步 题解报告【lca+树上统计(桶)】

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

  6. NOIP2016 天天爱跑步 线段树合并_桶_思维题

    竟然独自想出来了,好开心 Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r&q ...

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

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

  8. noip2016天天爱跑步

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

  9. P1600 天天爱跑步[桶+LCA+树上差分]

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

随机推荐

  1. 问题集 - console.log在IE下不可用

    js中添加如下一段代码即可. if(!window.console){ window.console = {}; } if(!window.console.log){ window.console.l ...

  2. windows安装logstash-input-jdbc并使用其导入MMSQL数据

    1.安装logstash 2.修改logstash 文件夹下Gemfile文件 将source改为:https://gems.ruby-china.org 3.进入bin目录 执行logstash-p ...

  3. NavRouter

    使用方法只需要跟vue-router一样正常使用即可,这里我们新加了一个路由跳转方法nav: router.nav()//参数同router.replace一样. 路由跳转策略 首先说下路由跳转过程, ...

  4. 使用git bash编译安装sysbench时遇到的坑

      Preface       When I was compiling the sysbench just now,I encountered some failures in the preced ...

  5. Adobe Photoshop CC2018最新教程+某宝店铺装修教程

    PS免费教程,ps淘宝店铺装修教程.该资源为本人从某商网站重金买来,现免费分享给大家,下载地址:百度网盘,https://pan.baidu.com/s/127PjFbGwVVUVce1litHFsw

  6. 「日常温习」Hungary算法解决二分图相关问题

    前言 二分图的重点在于建模.以下的题目大家可以清晰的看出来这一点.代码相似度很高,但是思路基本上是各不相同. 题目 HDU 1179 Ollivanders: Makers of Fine Wands ...

  7. Selenium(Python)调用pywin32上传图片

    import unittestfrom time import sleep import osfrom selenium import webdriverimport win32apiimport w ...

  8. 【CSVRead】-jmeter

    csv     read 读取文件

  9. 【setUp-tearDown】线程组开始,结束各执行一次

    使用setUp线程组的方式  ——> 开始 使用tearDown线程组 的方式 ——>结束

  10. Paper Reading - Convolutional Image Captioning ( CVPR 2018 )

    Link of the Paper: https://arxiv.org/abs/1711.09151 Motivation: LSTM units are complex and inherentl ...