Description

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

Input

第一行有两个整数N和M 。其中N代表树的结点数量, 同时也是观察员的数量, M代表玩家的数量。
接下来n-1 行每行两个整数U和V ,表示结点U 到结点V 有一条边。
接下来一行N 个整数,其中第个整数为Wj , 表示结点出现观察员的时间。
接下来 M行,每行两个整数Si和Ti,表示一个玩家的起点和终点。
对于所有的数据,保证 。
1<=Si,Ti<=N,0<=Wj<=N

Output

输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。

Sample Input

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6

Sample Output

2 0 0 1 1 1

lydrainbowcat讲的真是炒鸡棒啊,这里主要参考他在sfjsjjzn上的讲解

首先,每个玩家跑步的路线可以分成两部分:$S$->$lca(S,T)$  $lca(S,T)$->$T$,后者不包括lca

那么,如果位于节点x的观察员能看到玩家i,当且仅当满足:

1.x在S到lca的路径上,且满足$dep[S_i]-dep[x]=w[x]$

2.x在lca到T的路径上(不含lca),且满足$dep[S_i]+dep[x]-2*dep[lca]=w[x]$

接下来分开计算这两种观察员,最后相加即可

首先看一道题

我们的这道题也可以转化成“路径上投放物品”的问题

以第一种观察员的情况为例

由$dep[S_i]-dep[x]=w[x]$

可得$dep[S_i]=dep[x]+w[x]$

就可以理解为给S到lca的路径上的每个点添加类型为$dep[S_i]$的物品

所求为每个点$w[x]+dep[x]$类型的物品有多少

使用树上差分,将其转化为:

点S处生成物品$dep[S_i]$,点lca处这种物品消失

同理,第二种观察员可以转化为$dep[S_i]-2*dep[lca]$在T生成,在lca消失

所求为$w[x]-dep[x]$的物品数量

权值线段树合并确实是可以的,但本题不需要维护最值,只是求特定数量

所以可以对每个点开vector,把物品的生成和消失记录存在里面

最后统计的时候  开一个数组cnt[]用于统计数量(不是每个点开一个!运用差分的思想!)

递归时先存一下旧的$c[所求]$,回溯的时候用新的值减去它

即为子树和

最后每个点的答案即为两种情况相加

自认为代码可读性还是很高的:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=300005;
int n,m,to[N<<1],nxt[N<<1],head[N],fa[N][23],tot=0;
int w[N],dep[N],cnt1[N<<1],cnt2[N<<1],ans1[N],ans2[N];
vector<int> sp[3][N],del[3][N];
inline int read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;
}
void add(int x,int y)
{to[++tot]=y;nxt[tot]=head[x];head[x]=tot;
}
void dfs(int x,int deep)
{dep[x]=deep;for(int i=1;i<=20;i++)fa[x][i]=fa[fa[x][i-1]][i-1];for(int i=head[x];i;i=nxt[i]){if(dep[to[i]])continue;fa[to[i]][0]=x;dfs(to[i],deep+1);}
}
int LCA(int x,int y)
{if(dep[x]>dep[y])swap(x,y);for(int i=20;i>=0;i--)if(dep[fa[y][i]]>=dep[x])y=fa[y][i];if(x==y)return x;for(int i=20;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];return fa[x][0];
}
void spawn(int pos,int val,int k)
{sp[k][pos].push_back(val);
}
void remove(int pos,int val,int k)
{del[k][pos].push_back(val);
}
void cacl1(int x)
{int now=cnt1[w[x]+dep[x]+n];for(int i=0;i<(int)sp[1][x].size();i++)cnt1[sp[1][x][i]]++;for(int i=0;i<(int)del[1][x].size();i++)cnt1[del[1][x][i]]--;for(int i=head[x];i;i=nxt[i])if(dep[to[i]]>dep[x])cacl1(to[i]);ans1[x]=cnt1[w[x]+dep[x]+n]-now;
}
void cacl2(int x)
{int now=cnt2[w[x]-dep[x]+n];for(int i=0;i<(int)sp[2][x].size();i++)cnt2[sp[2][x][i]]++;for(int i=0;i<(int)del[2][x].size();i++)cnt2[del[2][x][i]]--;for(int i=head[x];i;i=nxt[i])if(dep[to[i]]>dep[x])cacl2(to[i]);ans2[x]=cnt2[w[x]-dep[x]+n]-now;
}
int main()
{n=read();m=read();for(int i=1;i<n;i++){int x=read(),y=read();add(x,y);add(y,x);}for(int i=1;i<=n;i++)w[i]=read();dfs(1,1);for(int i=1;i<=m;i++){int S=read(),T=read(),lca=LCA(S,T);spawn(S,dep[S]+n,1);remove(fa[lca][0],dep[S]+n,1);spawn(T,dep[S]-2*dep[lca]+n,2);remove(lca,dep[S]-2*dep[lca]+n,2);}cacl1(1);cacl2(1);for(int i=1;i<=n;i++)printf("%d ",ans1[i]+ans2[i]);return 0;
}

在自家oj上用时是最慢ac的一半,可以说是性价比极高了?

转载于:https://www.cnblogs.com/Rorschach-XR/p/11172940.html

[NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)相关推荐

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

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

  2. 天天爱跑步【树上差分】

    题目链接 很好的一道题目,主要是要想到这个方向. 首先,很容易想到的是,S到T的路径可以拆分成S~LCA和LCA~T这两条,现在,我们假设有一个点x满足条件,那么可以列出怎样的恒等式呢? 表示x到S的 ...

  3. NOIP2016天天爱跑步

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

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

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

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

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

  6. $NOIP2016$天天爱跑步

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

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

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

  8. [noip2016]天天爱跑步

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

  9. [noip2016]天天爱跑步(主席树+lca)

    恩..在百度的第一页翻了翻,没有用主席树做的,于是打算水一篇blog: 天天爱跑步 首先有一个解题的关键: 将玩家的向上和向下分成两部分: 先定义几个变量,s是起点,t是终点,wi是每个观察员出现的时 ...

最新文章

  1. 阿里工程师力荐的计算机网络和算法资料,限时下载!
  2. 成人高考报计算机还是学前教育,成人高考读什么专业比较好 三大热门专业
  3. 烧钱大战数以亿计 无人驾驶无法突破局限?
  4. 产品经理入门——PM
  5. window.open 不显示地址栏_谷歌浏览器Chrome显示「由贵单位管理」怎么解决?非阿里...
  6. linux中fstab文件详解
  7. python多继承_python作用域和多继承
  8. jQuery分别获取选中的复选框值
  9. 计算机开机后黑屏 只有鼠标,开机黑屏只有鼠标
  10. linux alias别名管理
  11. 计算从1加到该整数的值c语言,C语言编程题库与解析1.doc
  12. 记录一个小程序 input输入框格式手机号方法
  13. c语言英文字母降序排列,英文单词排序
  14. html九九乘法口诀表代码,JavaScript九九乘法口诀表的简单实现
  15. 云盘服务器被毁,360云盘宣布停止个人云盘服务 明年2月1日清空
  16. 关于重装系统后开始菜单栏anaconda3文件夹消失问题的解决办法
  17. oracle删除redo 未重启,卸载金山和瑞星后未重启就安装了卡巴,系统进不去了
  18. 苦心志,劳筋骨,饿体肤,乏其身,乱其所为
  19. Python网络编程之初识
  20. 图像语义分割实践(三)模型搭建与实现

热门文章

  1. 截流式合流制设计流量计算_截流式合流制管渠的水力计算要点
  2. 还没有表白神器?情人节来喽,快为心爱的她送上一份专属的礼物吧~
  3. 《Adobe Premiere Pro CS6中文版经典教程》——复习题
  4. php编辑器浅色还是深色,程序员的IDE到底是浅色主题好还是深色主题好?
  5. python图片截取斜四边形_python shapely.geometry.polygon任意两个四边形的IOU计算实例...
  6. centos怎么把计算机调到桌面,CentOS下命令行和桌面模式的切换方法
  7. 鸿蒙os系统测评,鸿蒙OS 2首批评价出炉,看已升级用户都怎么说的
  8. ​2 万字系统总结,带你实现 Linux 命令自由?
  9. 【三星篇】三星手机实用功能软件推荐
  10. Java中识别二维码并且提高二维码的识别率