传送门

题目分析:

一年前还是个傻子的时候居然直接放弃了这题。

首先列出两个方程:如果i节点的观察员能够观察到由s->t的那个人,那么:

\[dep[s] - dep[i] = w[i], dep[t] - dep[i] = len - w[i]
\]

整理得到:$$dep[s] = w[i] + dep[i] (1), dep[t] - len = dep[i] - w[i] (2)$$

也就是说:只要起点满足方程(1)和终点满足方程(2)的都能被i看到,下面考虑下差分:因为树的dfs是连续的,所以以节点i为根节点的子树的答案就是:扫描子树后的 - 扫描子树前的,对于一个人从s->t,在s节点+dep[s],在t节点+dep[t] - len,在lca节点-dep[s], 在fa[lca]节点-(dep[t] - len)(s+, t+, lca-, fa[lca]-,这样才能做到补充不漏,自行脑补),每次处理该点的差分标记更新cnt1和cnt2数组(cnt1表示关于起点的差分值的个数,cnt2表示关于终点的差分值的个数,对应上面的方程),节点i的答案就是:$$(扫描后的cnt1[w[i] + dep[i]] + 扫描后的cnt2[dep[i] - w[i]]) - (扫描前的cnt1[w[i] + dep[i]] + 扫描前的cnt2[dep[i] - w[i]])$$

最后还要注意cnt数组记录的差分值可能出现负数,所以要给作差的差分值+offset,来保证下标是合法的。

code

#include<bits/stdc++.h>
using namespace std; typedef long long ll; namespace IO{
inline ll read(){
ll i = 0, f = 1; char ch = getchar();
for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
if(ch == '-') f = -1, ch = getchar();
for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
return i * f;
}
inline void wr(ll x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
}using namespace IO; const int N = 3e5 + 5, M = 3e5 + 5;
int n, m, ecnt, adj[N], nxt[M << 1], go[M << 1];
int cnt1[N * 3], cnt2[N * 3], w[N], offset;
int fa[N][25], dep[N], ans[N];
struct node{int type, val, flag;};
vector<node> tag[N]; inline void addEdge(int u, int v){nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;} inline void pre(int u, int f){
dep[u] = dep[f] + 1;
fa[u][0] = f;
for(int i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int v, e = adj[u]; e; e = nxt[e]){
if((v = go[e]) == f) continue;
pre(v, u);
}
} inline int getLca(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
int delta = dep[u] - dep[v];
for(int i = 20; i >= 0; i--) if((1 << i) & delta) u = fa[u][i];
if(u == v) return u;
for(int i = 20; i >= 0; i--) if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
} inline void addTag(int u, int v){
int lca = getLca(u, v), len = dep[u] + dep[v] - 2 * dep[lca];
tag[u].push_back((node){1, dep[u], 1}), tag[v].push_back((node){2, dep[v] - len + offset, 1});
tag[lca].push_back((node){1, dep[u], -1}), tag[fa[lca][0]].push_back((node){2, dep[v] - len + offset, -1});
} inline void dfs(int u, int f){
int cur = cnt1[w[u] + dep[u]] + cnt2[dep[u] - w[u] + offset];
for(int i = 0; i < tag[u].size(); i++){
if(tag[u][i].type == 1) cnt1[tag[u][i].val] += tag[u][i].flag;
else cnt2[tag[u][i].val] += tag[u][i].flag;
}
for(int v, e = adj[u]; e; e = nxt[e]){
if((v = go[e]) == f) continue;
dfs(v, u);
}
ans[u] = -cur + cnt1[w[u] + dep[u]] + cnt2[dep[u] - w[u] + offset];
} int main(){
freopen("h.in", "r", stdin);
n = read(), m = read(); offset = n * 3;
for(int i = 1; i < n; i++){int x = read(), y = read(); addEdge(x, y), addEdge(y, x);}
pre(1, 0);
for(int i = 1; i <= n; i++) w[i] = read();
for(int i = 1; i <= m; i++){int u = read(), v = read(); addTag(u, v);}
dfs(1, 0);
for(int i = 1; i <= n; i++) wr(ans[i]), putchar(' ');
return 0;
}

NOIP2016 天天爱跑步 - 树上差分的更多相关文章

  1. 洛谷 1600 (NOIp2016) 天天爱跑步——树上差分

    题目:https://www.luogu.org/problemnew/show/P1600 看TJ:https://blog.csdn.net/clove_unique/article/detail ...

  2. NOIP2016 天天爱跑步 (树上差分+dfs)

    题目大意:给你一颗树,树上每个点都有一个观察员,他们仅会在 w[i] 时刻出现,观察正在跑步的玩家 一共有m个玩家,他们分别从节点 s[i] 同时出发,以每秒跑一条边的速度,沿着到 t[i] 的唯一路 ...

  3. [NOIP2016]天天爱跑步(树上差分+线段树合并)

    将每个人跑步的路径拆分成x->lca,lca->y两条路径分别考虑: 对于在点i的观察点,这个人(s->t)能被观察到的充要条件为: 1.直向上的路径:w[i]=dep[s]-dep ...

  4. NOIP2016 Day1 T2 天天爱跑步(树上差分,LCA)

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

  5. 洛谷P1600 天天爱跑步——树上差分

    题目:https://www.luogu.org/problemnew/show/P1600 看博客:https://blog.csdn.net/clove_unique/article/detail ...

  6. 洛谷$P1600$ 天天爱跑步 树上差分

    正解:树上差分 解题报告: 传送门$QwQ$! 这题还挺妙的,,,我想了半天才会$kk$ 首先对一条链$S-T$,考虑先将它拆成$S-LCA$和$LCA-T$,分别做.因为总体上来说差不多接下来我就只 ...

  7. BZOJ 4719--天天爱跑步(LCA&差分)

    4719: [Noip2016]天天爱跑步 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1464  Solved: 490[Submit][Stat ...

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

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

  9. [Noip2016]天天爱跑步 LCA+DFS

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

随机推荐

  1. scrapy--介绍

    ​ Scrapy一个开源和协作的框架,其最初是为了页面抓取所设计的,使用它可以以快速.简单.可扩展的方式从网站中提取所需的数据.但目前Scrapy的用途十分广泛,可用于如数据挖掘.监测和自动化测试等领 ...

  2. 使用 Python 第三方库 daft 绘制 PGM 中的贝叶斯网络

    daft 的官方文档请见 DAFT:BEAUTIFULLY RENDERED PROBABILISTIC GRAPHICAL MODELS. from matplotlib import rc rc( ...

  3. Android 解决RecyclerView删除Item导致位置错乱的问题

    RecyclerView的刷新分为内容变化和结构变化,结构变化比如remove和insert等并不会导致viewholder的更新,所以有时候我们使用 notifyItemRemoved(positi ...

  4. linux开发板的启动

    转载:http://blog.csdn.net/mr_raptor/article/details/6555667 虽然有很多地方并不是很明白,但是可以先记下 嵌入式系统启动过程 转载 2014年09 ...

  5. TPS54232-------电源管理芯片

    TPS54232 DC DC开关稳压器 电源管理芯片 放大器俗称功放 注意看芯片的次序1~8是如何排布的,这个规律一般是固定的 也许我们整理多了就能发现引脚的宽度和长度都是规格好的. 下面是封装: 所 ...

  6. 2015,我的投资理财策略(股权众筹+P2P网贷+活期理财)

    纸币流行,尤其是当今中国的市场经济,纸币几乎是一直是贬值的,每个人的财富都在被不断地稀释,可能是被政府.如果你不注意保值增值,你就越来越穷.     当年的万元户,在今天看来就是一个笑话,其实不怎么好 ...

  7. 大战C100K之-Linux内核调优篇--转载

    原文地址:http://joyexpr.com/2013/11/22/c100k-4-kernel-tuning/ 早期的系统,系统资源包括CPU.内存等都是非常有限的,系统为了保持公平,默认要限制进 ...

  8. HDU 2587 - 很O_O的汉诺塔

    看题传送门 吐槽题目 叫什么很O_O的汉诺塔我还@.@呢. 本来是想过一段时间在来写题解的,不过有人找我要. 本来排名是第8的.然后搞了半天,弄到了第五.不过代码最短~ 截止目前就9个ID过,小小的成 ...

  9. 25、写一个USB摄像头驱动程序(有ioctrl分析)

    videobuf2-core.h中的vb2_buffer,记录了v4l2_buffer ,驱动可以对vb2_buffer的v4l2_buffer进行操控, vb2_buffer是v4l2框架层的代码, ...

  10. swift学习第九天:可选类型以及应用场景

    可选类型的介绍 注意: 可选类型时swift中较理解的一个知识点 暂时先了解,多利用Xcode的提示来使用 随着学习的深入,慢慢理解其中的原理和好处 概念: 在OC开发中,如果一个变量暂停不使用,可以 ...