n<=300000个点的树,每个点有个人于第Ti秒观测,有m<=300000个人于时间0开始从Sj跑到Tj,速度1个点每秒,输出每个点上的人观察到的跑步的人的数量。

前25分:直接模拟每条路径,先s跑到lca再跑到t,边跑边记时间,如果经过某个点时时间刚好一样就该点答案++。

Si等于1的20分:观察人能观察到,只有他观察的时间和深度相等的时候。而一个跑步人跑下来就是这条链上满足“观察时间等于深度”的点答案+1,这可以用一个差分标记解决,如下图。

这样,最后从根节点开始向下dfs,一路上把标记加上,满足“观察时间等于深度”的点的答案就是当前的标记,而其他的点答案为0。

树变成一条链的15分:这启发着我们去追寻观察人的信息和跑步人的信息的关系。若有个人从Si跑到Ti,那么观测点j能观测到的条件就是j-Si=Wj且j<=Ti。那就是找j-Wj=Si的。类似于刚才的差分标记,把一个Si->Ti记两个标记,在Si处把数组中下标Si++,在Ti+1处--,扫过来的时候,把标记处理完,再查找下标j-Wj的点有几个,就可以直接回答询问。

最后的分:其实会以上这些,再不写正解就可惜了,因为推导的过程都差不多,方法也差不多。现在来看一个路径:

一个路径其实就一上一下两个过程,在向上经过j点时观察到的条件是:dep(Si)-dep(j)=Wj,dep()表示深度,Wj为观测时间。接下来到k那边和Si就无关了,但与之相关的Ti:dep(Ti)-dep(k)=Li-Wk,其中Li表示路径i的长度,也就是到Ti的时间,这段时间差恰好等于Ti到k的长度时可以观测到。整理两个式子,dep(Si)=dep(j)+Wj,dep(Ti)-Li=dep(k)-Wk。这么看来,回答一个询问,实际上是找这个观测点的子树里有多少个i满足上面两个式子。

不过这样是有偏差的。例如这样:

Si和Ti尽管是j的子树内的点,但他们对答案没影响。而j的孩子点,Si和Ti在这里会被计算两次。也就是要想个办法把Si和Ti能影响的范围表示出来。

可以发现,根据dfs的顺序,如果在Si处把对应信息+1,Ti处+1,那么在lca(Si,Ti)处应-1,lca的父亲那里再-1,这样一来,就可以使得Si到Ti的路径上每个点统计答案时只算到他们一次。

也就是说,开A,B两个数组存dep(j)+Wj和dep(j)-Wj两种信息,把一个跑步人拆成四个信息变化:在Si处,A数组中信息点dep(Si)加一;在Ti处,B数组中信息点dep(Ti)-Li加一;在lca(Si,Ti)处,A数组中信息点dep(Si)减一;在lca的父亲那里,B数组中信息点dep(Ti)-Li减一。信息点的变化以邻接表的形式连在它应该在的位置,在访问一棵子树时,先遍历子树,再把这个点的所有信息点变化处理好,然后在A,B两个数组中直接查对应值dep(j)+Wj,dep(j)-Wj的信息,这个信息就是答案。

具体见代码。由于B数组里的信息点涉及减法,故把B数组里的值都加上了n,查询时也加上n即可。

 #include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
//#include<iostream>
using namespace std; int n,m;
#define maxn 300011
struct Edge{int to,next;}edge[maxn*];int first[maxn],le=;
int dep[maxn],fa[maxn][];
void in(int x,int y) {Edge &e=edge[le];e.to=y;e.next=first[x];first[x]=le++;}
void insert(int x,int y) {in(x,y);in(y,x);}
void dfs(int x,int f)
{
dep[x]=dep[f]+;fa[x][]=f;
for (int i=first[x];i;i=edge[i].next)
{
const Edge &e=edge[i];
if (e.to!=f) dfs(e.to,x);
}
}
void makef()
{
for (int j=;j<=;j++)
for (int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) {int t=x;x=y;y=t;}
for (int j=;j>=;j--) if (dep[fa[x][j]]>=dep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=;j>=;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][];
}
struct List{bool sig,add;int v,next;}list[maxn*];int eve[maxn],ll=;
void addeve(int x,bool sig,bool add,int v)
{
List &l=list[ll];l.sig=sig;l.add=add;l.v=v;
l.next=eve[x];eve[x]=ll++;
}
int wa[maxn],pos[maxn*],neg[maxn*],ans[maxn];
void play(int x,int f)
{
ans[x]=pos[wa[x]+dep[x]]+neg[wa[x]-dep[x]+n];
for (int i=first[x];i;i=edge[i].next)
{
const Edge &e=edge[i];
if (e.to!=f) play(e.to,x);
}
for (int i=eve[x];i;i=list[i].next)
{
const List &e=list[i];
if (e.sig) pos[e.v]+=e.add?:-;
else neg[e.v]+=e.add?:-;
}
ans[x]=pos[wa[x]+dep[x]]+neg[wa[x]-dep[x]+n]-ans[x];
}
int x,y;
int main()
{
scanf("%d%d",&n,&m);
memset(first,,sizeof(first));
memset(eve,,sizeof(eve));
for (int i=;i<n;i++)
{
scanf("%d%d",&x,&y);
insert(x,y);
}
dfs(n/+,);makef();
for (int i=;i<=n;i++) scanf("%d",&wa[i]);
for (int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
int l=lca(x,y),d=dep[x]+dep[y]-*dep[l];
addeve(x,,,dep[x]);
addeve(y,,,d-dep[y]+n);
addeve(l,,,dep[x]);
addeve(fa[l][],,,d-dep[y]+n);
}
memset(pos,,sizeof(pos));
memset(neg,,sizeof(neg));
play(n/+,);
for (int i=;i<n;i++) printf("%d ",ans[i]);printf("%d",ans[n]);
return ;
}

自测在vijos上取n/2+3为根可以通过所有的点。

NOIP2016提高组D1T2 天天爱跑步的更多相关文章

  1. BZOJ4719[NOIP2016提高组Day1T2] 天天爱跑步

    #261. [NOIP2016]天天爱跑步 描述 提交 自定义测试 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家 ...

  2. NOIP2016提高组Day1T2 天天爱跑步 树链剖分 LCA 倍增 差分

    原文链接https://www.cnblogs.com/zhouzhendong/p/9275606.html 题目传送门 - 洛谷P1600 题目传送门 - LOJ#2359 题目传送门 - Vij ...

  3. 【NOIP】提高组2016 天天爱跑步

    [题意]n个点的树,有m个人同时开始走链,每一步花一秒,n个点都有观察员在ai秒观察,求每个观察员观察到的人数. [算法]树上差分(主席树||线段树合并) [题解]一个人的走链可以拆成u-lca和lc ...

  4. NOIP2016提高组解题报告

    NOIP2016提高组解题报告 更正:NOIP day1 T2天天爱跑步 解题思路见代码. NOIP2016代码整合

  5. 【题解】NOIP2016提高组 复赛

    [题解]NOIP2016提高组 复赛 传送门: 玩具谜题 \(\text{[P1563]}\) 天天爱跑步 \(\text{[P1600]}\) 换教室 \(\text{[P1850]}\) 组合数问 ...

  6. 【题解】NOIP2016 提高组 简要题解

    [题解]NOIP2016 提高组 简要题解 玩具迷题(送分) 用异或实现 //@winlere #include<iostream> #include<cstdio> #inc ...

  7. 【NOIP2016】DAY1 T2 天天爱跑步

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

  8. 【NOIP2016提高组复赛day2】天天爱跑步

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

  9. [日记&做题记录]-Noip2016提高组复赛 倒数十天

    写这篇博客的时候有点激动 为了让自己不颓 还是写写日记 存存模板 Nov.8 2016 今天早上买了两个蛋挞 吃了一个 然后就做数论(前天晚上还是想放弃数论 但是昨天被数论虐了 woc noip模拟赛 ...

随机推荐

  1. windows下oracle 11g r2 安装过程与卸载详细图解

    Oracle 11g安装 1.解压下载的包,然后进入包内,点击setup.exe开始安装 . 2.出现如下:一般把那个小对勾取消,点击下一步进行, 弹出下图这个后点‘是' 3.下图后,选择创建和配置数 ...

  2. 从一个线上服务器警告谈谈backlog

    缘起 双十一如期而至,此时的我因为在处理客户的一个问题已经陷入了忙碌.突然,不断接到驻场实施发来的反馈,都是相同的反馈--"客户端操作缓慢". 我现在负责的服务器是一台接口服务器, ...

  3. react children技巧总结

    在使用该技巧时,建议先看一下相关的知识,点我查看 假如使用该属性时,想把父组件的所有属性及部分方法传递给子组件,该怎么办呢?看代码 const Child = ({ doSomething, valu ...

  4. 深入Python的类和对象

    多态:不同的子类对象,可以调用相同的父类方法,通过改写父类的方法,产生不同的执行结果 instance和type的区别: instance能够顺延到父类,比对对象与父类是否类型一致.而type只能比对 ...

  5. (vue01)vue环境搭建

    腾讯,百度,网易....这么大媒体平台咋老推送这么lower的信息? 你们不缺钱啊....我这么善良的孩子都别你们带坏了 强烈鄙视马化腾 强烈鄙视李彦宏 参考地址: https://segmentfa ...

  6. Visual Studio 2017离线安装包

    点击下载

  7. flask - 1

    from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello, Worl ...

  8. pta函数作业

    7-10 设计思路:本题需要判断一个正整数数是否为素数,所谓素数,就是除一和本身外没有其他因数的数.具体判断过程如下:对于一个大于一的整数,从2开始用循环计数i去除此数,若余数不为零,则循环计数i自加 ...

  9. REST接口设计规范

    URI格式规范 URI(Uniform Resource Identifiers) 统一资源标示符 URL(Uniform Resource Locator) 统一资源定位符 URI的格式定义如下: ...

  10. 使用 TListView 控件(2)

    本例效果图: 代码文件: unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, ...