~~~题面~~~

题解:

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

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

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

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

但是还有更好的算法,是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[12000100], *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 = 0; char c = getchar();
 23     while(c > '9' || c < '0') c = getchar();
 24     while(c >= '0' && c <= '9') x = x * 10 + c - '0', 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 = 1; i < n; i++)
 49     {
 50         a = read(), b = read();
 51         add(a, b);
 52     }
 53     for(R i = 1; i <= n; i++) w[i] = read();
 54     E1.tot = E2.tot = tot = 1;
 55     for(R i = 1; i <= m; i++)
 56     {
 57         s[i] = read(), t[i] = read();
 58         E1.add(s[i], t[i]);
 59         add1(s[i], i, 0);//标记为开始节点
 60         add1(t[i], i, 1);//标记为结束节点
 61         id[E1.tot - 1] = 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] + 1;
 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 ^ 1])
 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 = 1; i <= n; i++) father[i] = i;
101         dep[1] = 1;
102         dfs(1);
103     }
104 }LCA;
105
106 #define k 600000
107 struct difference{
108     int bu[ac], b[ac * 3];
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 = 1; 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, 1, 12000000, stdin);
146     pre();
147     LCA.getLCA();
148     get.dfs(1, 0);
149     work();
150     fclose(stdin);
151     return 0;
152 }

转载于:https://www.cnblogs.com/ww3113306/p/9382100.html

[NOIP2016] 天天爱跑步 桶 + DFS相关推荐

  1. [BZOJ4719][P1600][NOIP2016]天天爱跑步[LCA+dfs序+差分]

    题意:一棵树,有 \(m\) 个人从 \(s_i\) 到 \(t_i\) 跑步,每个人的速度都是1,每个点有一个观察员 当一个人在w[i]经过第 \(i\) 个点的时候 第 \(i\) 个点的答案+1 ...

  2. [NOIP2016]天天爱跑步(lca+乱搞)

    2557. [NOIP2016]天天爱跑步 时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑 ...

  3. $NOIP2016$天天爱跑步

    \(NOIP2016\)天天爱跑步 真\(™\)是\(NOIP\)近几年中最难的一道-- 先看一下部分分,有起点终点为根节点的分值,想到拆路径,对于路径\((S,T)\),拆为\((S,LCA)\)和 ...

  4. NOIP2016天天爱跑步

    NOIP2016天天爱跑步 这题一看显然lca+树上差分,但是因为有w的限制不能直接加,所以考虑权值线段树合并, 每个选手的起点终点对于不同的节点的影响是不同的,这就非常麻烦了,但是可以发现无论如何他 ...

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

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

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

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

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

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

  8. 【题解】NOIP-2016 天天爱跑步

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

  9. [NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)

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

  10. [noip2016]天天爱跑步

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

最新文章

  1. 英文首字母排序mysql_利用MySQL数据库来处理中英文取首字母排序
  2. rsyslog日志管理+LogAnalyzer
  3. Mysql双向同步复制
  4. win2k cannot set the docm proportites
  5. 技术干货-PCB彩印教程(水转印)
  6. word使用技巧(不定时更新)
  7. “快准顺”而不是“信达雅”
  8. WSP (无线会话协议)
  9. python闭包(一分钟读懂)
  10. 在php中isset什么意思,php – isset()和__isset()之间有什么区别?
  11. OpenCV——无法打开“opencv2/opencv.hpp”文件
  12. Lintcode A+B问题
  13. 双向链表的结点增删细节(p->next->prior = s是啥意思,p->next->prior究竟代表下一个结点的prior指针还是p本身)
  14. css字体毛边属性设置
  15. Java地位被撼动?Java与JavaScript的趣事连载
  16. To刘卓岭:在这里讨论下吧
  17. compact php,php compact 通过变量创建数组
  18. C++学习笔记(十)——String类
  19. Android-Framework-GPS定位原理和修改
  20. 中级计算机职称哪个可以挂靠,2016年中级职称挂靠哪个证书更值钱?

热门文章

  1. qt新建html5,QT Creator无法创建纯C++项目或HTML5项目(QT Creator直接关闭)
  2. 中小型网络工程设计与实现_小型网络如何实现经济可靠的设计和部署 (一)...
  3. hive left join入门
  4. 常见数据结构总结,持续更新...
  5. 通过Ajax进行POST提交JSON类型的数据到SpringMVC Controller的方法
  6. CentOS6.8 x86_64bit安装nginx-1.6.2
  7. 我的电脑能装苹果吗?
  8. wince只运行一次应用程序
  9. 第八篇: UpdateProgress 控件--显示正在处理中的信息
  10. 2019swpuj2ee作业3