[NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)
Description
Input
Output
输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。
Sample Input
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
Sample Output
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=;
int n,m,to[N<<],nxt[N<<],head[N],fa[N][],tot=;
int w[N],dep[N],cnt1[N<<],cnt2[N<<],ans1[N],ans2[N];
vector<int> sp[][N],del[][N];
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>'')
{if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<='')
{x=(x<<)+(x<<)+ch-'';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=;i<=;i++)
fa[x][i]=fa[fa[x][i-]][i-];
for(int i=head[x];i;i=nxt[i])
{
if(dep[to[i]])continue;
fa[to[i]][]=x;
dfs(to[i],deep+);
}
}
int LCA(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
for(int i=;i>=;i--)
if(dep[fa[y][i]]>=dep[x])y=fa[y][i];
if(x==y)return x;
for(int i=;i>=;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][];
}
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=;i<(int)sp[][x].size();i++)
cnt1[sp[][x][i]]++;
for(int i=;i<(int)del[][x].size();i++)
cnt1[del[][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=;i<(int)sp[][x].size();i++)
cnt2[sp[][x][i]]++;
for(int i=;i<(int)del[][x].size();i++)
cnt2[del[][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=;i<n;i++)
{
int x=read(),y=read();
add(x,y);add(y,x);
}
for(int i=;i<=n;i++)
w[i]=read();
dfs(,);
for(int i=;i<=m;i++)
{
int S=read(),T=read(),lca=LCA(S,T);
spawn(S,dep[S]+n,);
remove(fa[lca][],dep[S]+n,);
spawn(T,dep[S]-*dep[lca]+n,);
remove(lca,dep[S]-*dep[lca]+n,);
}
cacl1();
cacl2();
for(int i=;i<=n;i++)
printf("%d ",ans1[i]+ans2[i]);
return ;
}
在自家oj上用时是最慢ac的一半,可以说是性价比极高了?
[NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)的更多相关文章
- [luogu1600 noip2016] 天天爱跑步 (树上差分)
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵 ...
- ☆ [NOIp2016] 天天爱跑步 「树上差分」
题目类型:LCA+思维 传送门:>Here< 题意:给出一棵树,有\(M\)个人在这棵树上跑步.每个人都从自己的起点\(s[i]\)跑到终点\(t[i]\),跑过一条边的时间为1秒.现在每 ...
- bzoj 4719: [Noip2016]天天爱跑步【树上差分+dfs】
长久以来的心理阴影?但是其实非常简单-- 预处理出deep和每组st的lca,在这里我简单粗暴的拿树剖爆算了 然后考虑对于一组s t lca来说,被这组贡献的观察员x当且仅当: x在s到lca的路径上 ...
- NOIP2016 天天爱跑步(树上差分)
题意 给定一棵树,从时刻 0 开始,有若干人从 S[i] 出发向 T[i] 移动,每单位时刻移动一条边 对于树上每个点 x,求 w[x] 时刻有多少人恰好路过 x N,M≤300000 题解 从上午 ...
- NOIP2016天天爱跑步 题解报告【lca+树上统计(桶)】
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn个 ...
- 【NOIP2016】天天爱跑步(树上差分)
题意: 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.?天天爱跑步?是一个养成类游戏,需要 玩家每天按时上线,完成打卡任务.这个游戏的地图可以看作一一棵包含 N个结点 ...
- [NOIP2016]天天爱跑步-题解
题面传送门 解答 设第\(j\)号玩家在\(V_j\)时刻出发. 弱化问题:如果树退化成了一条链.则在\(j\)处的观察员能观察到的\(i\)号玩家当且仅当 \[ i玩家经过j,且 \begin{ca ...
- LOJ2359. 「NOIP2016」天天爱跑步【树上差分】
LINK 思路 首先发现如果对于一个节点,假设一个节点需要统计从字数内来的贡献 需要满足\(dep_u - dep_s = w_u\) 这个条件其实可以转化成\(dep_u - w_u = dep_s ...
- P1600 [NOIP2016 提高组] 天天爱跑步 (树上差分)
对于一条路径,s-t,位于该路径上的观察员能观察到运动员当且仅当以下两种情况成立:(d[ ]表示节点深度) 1.观察员x在s-lca(s,t)上时,满足d[s]=d[x]+w[x]就能观察到,所以我们 ...
随机推荐
- NRF24L01学习
一.工作模式 PWR_UP和PRIM_RX在寄存器0x00中的第0位和第1位. 待机模式I:只是使用晶振工作,CE=0时是拉低? 待机模式II:部分时钟缓冲器处在工作模式.TX FIFO为空并且CE为 ...
- GPIO软件模拟IIC时序
一.MPU6050中的IIC时序 1.1 START和STOP SDA和SCL在高电平时,SDA拉低表示START.SCL拉低,表示可以传输数据. SDA和SCL在低电平时,SDA拉高表示STOP. ...
- hdu 4336 Card Collector(状压dp/Min-Max反演)
传送门 解题思路 第一种方法是状压\(dp\),设\(f(S)\)为状态\(S\)到取完的期望步数,那么\(f(S)\)可以被自己转移到,还可以被\(f(S|(1<<i))\)转移到,\( ...
- CSS 的基础语法
1.基础语法规则 CSS 规则由两个主要的部分构成:选择器,以及一条或多条声明. 每条声明由一个属性和一个值组成.每个属性有一个值.属性和值被冒号分开. selector {property: val ...
- python singleton 4种单例
def singleton(cls, *args, **kwargs): instances = {} def inner(cls, *args, **kwargs): if cls not in i ...
- 「2020 新手必备 」极速入门 Retrofit + OkHttp 网络框架到实战,这一篇就够了!
老生常谈 什么是 Retrofit ? Retrofit 早已不是什么新技术了,想必看到这篇博客的大家都早已熟知,这里就不啰嗦了,简单介绍下: Retrofit 是一个针对 Java 和 Androi ...
- 怎么知道dll文件是哪个net版本
有时候经常需要查看.dll所使用的.net版本, 因为根本不知道它是使用了1.1还是2.0, 或者是3.0, 这个时候如果需要打开vs.net那又太麻烦, 所以经过长久的摸索, 我找到了一个比较简便的 ...
- python基础【第十篇】
Python文件操作 1.常规格式 f = open(file="文件所在路径/文件名",mode="操作模式",encoding="选择的编码&qu ...
- MATLAB图像uint8,uint16,double, rgb转灰度解释
1.uint8,uint16与double 为了节省存储空间,matlab为图像提供了特殊的数据类型uint8(8位无符号整数),以此方式存储的图像称作8位图像.matlab读入图像的数据是uint8 ...
- Python之小测试:用正则表达式写一个小爬虫用于保存贴吧里的所有图片
很简单的两步: 1.获取网页源代码 2.利用正则表达式提取出图片地址 3.下载 #!/usr/bin/python #coding=utf8 import re # 正则表达式 import urll ...