NOIP提高组2016 D1T2 【天天爱跑步】
码了一个下午加一个晚上吧。。。。。。
题目描述:
小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。
这个游戏的地图可以看作一一棵包含 n个结点和 n-1条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从1到n的连续正整数。
现在有m个玩家,第i个玩家的起点为 Si,终点为 Ti 。每天打卡任务开始时,所有玩家在第0秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)
小c想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点jj的观察员会选择在第Wj秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第Wj秒也理到达了结点j。 小c想知道每个观察员会观察到多少人?
注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一 段时间后再被观察员观察到。 即对于把结点j作为终点的玩家: 若他在第Wj秒前到达终点,则在结点j的观察员不能观察到该玩家;若他正好在第Wj秒到达终点,则在结点j的观察员可以观察到这个玩家。
数据范围:

思路分析:
某王 云:任何题目的正解都是由部分分推导出来的,部分分就是在引导你走向正解。
嗯,好,我们开始讲部分分。
25分:
和人一起跳就是,lca O(n)就行,都不带优化的。
20分(s[i]=1):
以根为起点,那么对于点u,显然只有当depth[u]=w[u]时,才有可能有路径能够对它产生贡献。
怎么样的路径才会对它产生贡献呢?——简单啊,t在以u为根的子树中就行了嘛。
那么这个值只要用树形DP搞一搞就行了。
20分(t[i]=1):
以根为终点,对于节点u,只有起点在以u为根的子树中且深度为depth[u]+w[u]的路径才会对点u产生贡献。
我们可以搞一个桶,把起点深度相同的点全部丢进同一个桶bac[depth[s]]。
那么当我们访问到节点u时,一个变量pre记录一下当前bac[depth[u]+w[u]]的值,同时bac[depth[u]]+=tot[u],其中tot[u]表示以u节点为起点的路径数量。
当我们要退出这个点时,ans[u]+=bac[depth[u]+w[u]]-pre。(“类似”差分)
吐槽一下:我刚做这题的时候,同学告诉我这题是差分,误导了我好久。。。。。。
100分(出现辣——正解!!!):
嗯,某王 其实说的很对,特别有道理!——没错,正解就是“综上所述”
把一条链分为两段,s到lca一段(这段是往上走的),lca到t一段(这段是往下的)。
先分类讨论一下:
对于一个节点u,我们将对其产生贡献的路径分为两种:1:自下而上经过它,从而产生贡献。2:自上而下经过它,从而产生贡献。
1、自下而上:
这个和t[i]=1其实类似。
能用这种方式对点u产生贡献的路径需要满足:
1、起点在以u为根的子树中,终点在u以及u的祖先中。
2、depth[s]=depth[u]+w[u]
然后像t[i]=1那样用桶,类差分一下就行了。
更新操作:
bac[depth[u]]+=tot[u]
细节处理:
当我们要退出一个节点u时要枚举所有以u节点为lca的路径,然后bac[depth[s]]--。
为什么呢?——因为条件1嘛!
当u节点退出后,就会进入u的父亲,而这条路径的上半段对于u的父亲与u的兄弟是没有贡献的(因为这段路径的起点(lca)并不在它们自己或是它们的祖先中了)。
2、自上而下:
这个稍微难一点点。
能对节点u产生贡献的路径,起点不一定是u节点的祖先,也有可能是u的兄弟(这个能想象吧,就是先往上走,再折下来的那种)。
但是能对节点u产生贡献的路径,他们的起点与u的距离肯定是w[u]。
所以需要满足:
1、终点在以u为根的子树中,起点不在以u为根的子树中。
2、 depth[s]+depth[u]-2*depth[lca(s,u)]=w[u]
depth[s]-2*depth[lca(s,t)]+depth[u]=w[u]
depth[s]+depth[t]-2*depth[lca(s,t)]+depth[u]-depth[t]=w[u]
len[s,t]-depth[t]=w[u]-depth[u]
这里讲一下为什么lca(s,u)=lca(s,t)吧,因为不管这条路径属于上面两种情况的哪一种,只要满足了条件1,那么depth[lca(s,t)]>=depth[u]对吧,而t又在以u为根的子树中,那么lca肯定不会变啊。
同样的,用桶类差分即可。
但是此时类差分的时候要注意:把s丢进bac[len[s,t]-depth[t]]中,询问时要找的桶是bac[w[u]-depth[u]](上面应该讲的很清楚了吧)。
更新操作:
退出一个节点时,枚举所有以u为终点的路径,然后bac[depth[len[s,t]-depth[t]]]++。
细节处理:
退出一个节点时,枚举所有以u为lca的路径,然后bac[depth[len[s,t]-depth[t]]]--。(这个和上面差不多就不讲了)
代码实现:
var
head:array[1..4,0..300000]of longint;
next,len,vet:array[1..2000000]of longint;
vis:array[0..300000]of boolean;
depth,ans,sum,w:array[0..300000]of longint;
bac:array[-300000..300000]of longint;
f:array[0..300000,0..20]of longint;
i,n,m,x,y,s,t,z,tot,l:longint;
procedure add(k,x,y,z:longint);
begin
inc(tot);
next[tot]:=head[k,x];
vet[tot]:=y;
head[k,x]:=tot;
len[tot]:=z;
end;
procedure dfs(u,dep:longint);
var
i,v:longint;
begin
depth[u]:=dep; vis[u]:=true;
for i:=1 to 20 do
f[u,i]:=f[f[u,i-1],i-1];
i:=head[1,u];
while i<>0 do
begin
v:=vet[i];
if not vis[v] then
begin f[v,0]:=u; dfs(v,dep+1); end;
i:=next[i];
end;
end;
function lca(a,b:longint):longint;
var
i,t:longint;
begin
if depth[a]>depth[b] then begin t:=a; a:=b; b:=t; end;
for i:=20 downto 0 do
if depth[f[b,i]]>=depth[a] then b:=f[b,i];
if a=b then exit(a);
for i:=20 downto 0 do
if f[a,i]<>f[b,i] then
begin a:=f[a,i]; b:=f[b,i]; end;
exit(f[a,0]);
end;
procedure up(u,father:longint);
var
i,v,s,pre:longint;
begin
pre:=bac[w[u]+depth[u]];
i:=head[1,u];
while i<>0 do
begin
v:=vet[i];
if v<>father then up(v,u);
i:=next[i];
end;
bac[depth[u]]:=bac[depth[u]]+sum[u];
ans[u]:=ans[u]+bac[w[u]+depth[u]]-pre;
i:=head[2,u];
while i<>0 do
begin s:=vet[i]; dec(bac[depth[s]]); i:=next[i]; end;
end;
procedure down(u,father:longint);
var
i,pre,s,t,v:longint;
begin
pre:=bac[w[u]-depth[u]];
i:=head[1,u];
while i<>0 do
begin
v:=vet[i];
if v<>father then down(v,u);
i:=next[i];
end;
i:=head[4,u];
while i<>0 do
begin t:=vet[i]; inc(bac[len[i]-depth[t]]); i:=next[i]; end;
ans[u]:=ans[u]+bac[w[u]-depth[u]]-pre;
i:=head[3,u];
while i<>0 do
begin t:=vet[i]; dec(bac[len[i]-depth[t]]); i:=next[i]; end;
end;
begin
read(n,m);
for i:=1 to n-1 do
begin
read(x,y);
add(1,x,y,0); add(1,y,x,0);
end;
dfs(1,1);
for i:=1 to n do read(w[i]);
for i:=1 to m do
begin
read(s,t); z:=lca(s,t); inc(sum[s]); l:=depth[s]+depth[t]-2*depth[z];
if depth[s]-depth[z]=w[z] then dec(ans[z]);
add(2,z,s,0); add(3,z,t,l); add(4,t,t,l);
end;
up(1,0);
fillchar(bac,sizeof(bac),0);
down(1,0);
for i:=1 to n do
write(ans[i],' ');
end. //感觉guide不好用。。。。。。
NOIP提高组2016 D1T2 【天天爱跑步】的更多相关文章
- 题解——洛谷P2827 NOIP提高组 2016 蚯蚓
队列模拟 详细题解待填坑 #include <cstdio> #include <algorithm> #include <queue> #include < ...
- NOIP提高组2016总结
前言 大翻车! 300--: day1 8:30~9:00, 照常看题,思考. 9:00~9:15, 搞定第一题,很水. 9:15~9:45, 思考第二题,我考虑用分深度来处理,想出个个玄学暴力,但刚 ...
- NOIP提高组2016 D2T3 【愤怒的小鸟】
貌似还没有写过状压DP的题目,嗯,刚好今天考了,就拿出来写一写吧. 题目大意: 额,比较懒,这次就不写了... 思路分析: 先教大家一种判断题目是不是状压DP的方法吧. 很简单,那就是--看数据范围! ...
- NOIP提高组初赛难题总结
NOIP提高组初赛难题总结 注:笔者开始写本文章时noip初赛新题型还未公布,故会含有一些比较老的内容,敬请谅解. 约定: 若无特殊说明,本文中未知数均为整数 [表达式] 表示:在表达式成立时它的值为 ...
- NOIP提高组2004 合并果子题解
NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消 ...
- 计蒜客 NOIP 提高组模拟竞赛第一试 补记
计蒜客 NOIP 提高组模拟竞赛第一试 补记 A. 广场车神 题目大意: 一个\(n\times m(n,m\le2000)\)的网格,初始时位于左下角的\((1,1)\)处,终点在右上角的\((n, ...
- 1043 方格取数 2000 noip 提高组
1043 方格取数 2000 noip 提高组 题目描述 Description 设有N*N的方格图(N<=10,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0.如下图所示(见样 ...
- [NOIP提高组2018]货币系统
[TOC] 题目名称:货币系统 来源:2018年NOIP提高组 链接 博客链接 CSDN 洛谷博客 洛谷题解 题目链接 LibreOJ(2951) 洛谷(P5020) 大视野在线评测(1425) 题目 ...
- 津津的储蓄计划 NOIp提高组2004
这个题目当年困扰了我许久,现在来反思一下 本文为博客园ShyButHandsome的原创作品,转载请注明出处 右边有目录,方便快速浏览 题目描述 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津\ ...
随机推荐
- java控制流程(一)
一.scanner: scanner可以获取用户的输入的信息 scanner的初始化: //导入的包 import java.util.Scanner; public class Test { pub ...
- OpenJ_Bailian - 2995-登山(两遍最长上升子序列+枚举顶点)
五一到了,PKU-ACM队组织大家去登山观光,队员们发现山上一个有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号.同时队员们还有另一个登山习惯,就是不连续 ...
- Java面试知识点1
typora-root-url: ......\Software\Typora\Picture Bean的作用域 在Spring的元素的scope属性设置bean的作用域,用来决定bean是单实例还是 ...
- 正则表达式与SQL
在我心中正则表达式和SQL就是一样的东西. SQL是结构化查询语言,是根据某个查询.修改规则来查询修改数据,是描述一个规则给数据库,数据库来执行, 数据库返回结果,过程不需要考虑,不算是编程语言. 正 ...
- Linux下日志文件过大解决方案
很多Linux服务器里的应用程序都是无间断的输出日志,这对于服务器的硬盘是一个很大的考验.良许之前也分享过一篇文章,介绍如何让应用程序在后台执行: linux后台执行命令:&与nohup的用法 ...
- JVM垃圾收集机制
JVM垃圾回收机制是java程序员必须要了解的知识,对于程序调优具有很大的帮助(同时也是大厂面试必问题). 要了解垃圾回收机制,主要从三个方面: (1)垃圾回收面向的对象是谁? (2)垃圾回收算法有哪 ...
- 计算几何(一):凸包问题(Convex Hull)
引言 首先介绍下什么是凸包?如下图: 在一个二维坐标系中,有若干点杂乱排列着,将最外层的点连接起来构成的凸多边型,它能包含给定的所有的点,这个多边形就是凸包. 实际上可以理解为用一个橡皮筋包含住所有给 ...
- 从C++入手,探寻java的特点
java的特点 java语言建立在成熟的算法语言和坚实的面向对象理论的基础上,具有强大的应用系统设计能力,其具备的跨平台特型,其具备的跨平台特型.面向对象和可靠性.安全性等特点是它能够充分适应网络需要 ...
- linux学习(十一)linux安装nginx
一.前言 由于本地练手的小demo用的是vue+spring boot来玩的,所以部署的时候想着用Nginx来实现反向代理[即请求转发,解决前后端分离的跨域请求问题],既然要用,那么首先得在服务器上面 ...
- [阅读笔记]Attention Is All You Need - Transformer结构
Transformer 本文介绍了Transformer结构, 是一种encoder-decoder, 用来处理序列问题, 常用在NLP相关问题中. 与传统的专门处理序列问题的encoder-deco ...