[NOIp2016]天天爱跑步

LG传送门

对于一个人,他的路程会分为两段,一段向上(根),一段向下,考虑在向上过程中他能产生贡献的观察者具有什么性质:设出发点深度为\(dep[x]\),观察者深度为\(dep[y]\),观察的时间为\(t\),需满足\(dep[x] - dep[y] = t\),换句话说就是\(dep[y] + t = dep[x]\)。向下那一段的推导类似,下面的部分也只以向上的路径为例来解释。

现在我们记录每个点下方出发点深度为\(dep[x]\)的人数,需要对于每个出发点更新出发点到出发点与目的地的lca的所有点,想到到开一颗权值线段树,用线段树合并不断把信息往上传,在lca处消除这次更新的影响,这样一来直接我们就可以在树上统计答案了。向下路径的处理与向上路径类似,这里就不再赘述,上代码。

#include <cstdio>
#include <cctype>
#include <vector>
#define R register
#define I inline
#define B 1000000
using namespace std;
const int N = 300001, M = 15000007;
char buf[B], *p1, *p2;
I char gc() { return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, B, stdin), p1 == p2) ? EOF : *p1++; }
I int rd() {
R int f = 0;
R char c = gc();
while (c < 48 || c > 57)
c = gc();
while (c > 47 && c < 58)
f = f * 10 + (c ^ 48), c = gc();
return f;
}
int w[N], s[N], t[N], p[N], q[N], d[N], r[N], u[N], v[N], o[N], n, C, S;
struct T{int f, p, q;}e[M];
vector <int> g[N];
I void swp(int &x, int &y) { x ^= y, y ^= x, x ^= y; }
void dfs1(int x, int f) {
p[x] = f, d[x] = d[f] + 1, t[x] = 1;
for (R int i = 0, y, m = 0; i < s[x]; ++i)
if ((y = g[x][i]) ^ f) {
dfs1(y, x), t[x] += t[y];
if (t[y] > m)
m = t[y], q[x] = y;
}
}
void dfs2(int x, int t) {
r[x] = t;
if (q[x])
dfs2(q[x], t);
for (R int i = 0, y; i < s[x]; ++i)
if ((y = g[x][i]) ^ p[x] && y ^ q[x])
dfs2(y, y);
}
I int lca(int x, int y) {
while (r[x] ^ r[y]) {
if (d[r[x]] < d[r[y]])
swp(x, y);
x = p[r[x]];
}
if (d[x] > d[y])
swp(x, y);
return x;
}
void ins(int &k, int l, int r, int x, int v) {
if (!k)
k = ++C;
e[k].f += v;
if (l == r)
return ;
R int m = l + r >> 1;
if (x <= m)
ins(e[k].p, l, m, x, v);
else
ins(e[k].q, m + 1, r, x, v);
}
int mrg(int k, int t) {
if (!k)
return t;
if (!t)
return k;
e[k].f += e[t].f, e[k].p = mrg(e[k].p, e[t].p), e[k].q = mrg(e[k].q, e[t].q);
return k;
}
int qry(int k, int l, int r, int x) {
if (l == r)
return e[k].f;
R int m = l + r >> 1;
if (x <= m)
return qry(e[k].p, l, m, x);
else
return qry(e[k].q, m + 1, r, x);
}
void dfs(int x) {
for (R int i = 0, y; i < s[x]; ++i)
if ((y = g[x][i]) ^ p[x])
dfs(y), u[x] = mrg(u[x], u[y]), v[x] = mrg(v[x], v[y]);
o[x] = qry(u[x], 1, S, w[x] + d[x]) + qry(v[x], 1, S, w[x] - d[x] + n);
}
int main() {
R int m, i, x, y, a;
n = rd(), m = rd(), S = n << 1;
for (i = 1; i < n; ++i)
x = rd(), y = rd(), g[x].push_back(y), g[y].push_back(x);
for (i = 1; i <= n; ++i)
s[i] = g[i].size(), w[i] = rd();
dfs1(1, 0), dfs2(1, 1);
for (i = 1; i <= m; ++i)
x = rd(), y = rd(), a = lca(x, y), ins(u[x], 1, S, d[x], 1), ins(u[a], 1, S, d[x], -1), ins(v[y], 1, S, d[x] - (d[a] << 1) + n, 1), ins(v[p[a]], 1, S, d[x] - (d[a] << 1) + n, -1);
dfs(1);
for (i = 1; i <= n; ++i)
printf("%d ", o[i]);
return 0;
}

因为减的结果可能有负数,所以加上一个\(n\),值域变两倍。

[NOIp2016]天天爱跑步 线段树合并的更多相关文章

  1. NOIP2016 天天爱跑步 线段树合并_桶_思维题

    竟然独自想出来了,好开心 Code: #include<bits/stdc++.h> #define setIO(s) freopen(s".in","r&q ...

  2. 2018.08.09 bzoj4719: [Noip2016]天天爱跑步(树链剖分)

    传送门 话说开始上文化课之后写题时间好少啊. 这道题将一个人的跑步路线拆成s->lca,lca->t,然后对于第一段上坡路径要经过的点,当前这个人能对它产生贡献当且仅当dep[s]-dep ...

  3. LOJ #2359. 「NOIP2016」天天爱跑步(倍增+线段树合并)

    题意 LOJ #2359. 「NOIP2016」天天爱跑步 题解 考虑把一个玩家的路径 \((x, y)\) 拆成两条,一条是 \(x\) 到 \(lca\) ( \(x, y\) 最近公共祖先) 的 ...

  4. [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]

    [NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...

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

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

  6. BZOJ4719 NOIP2016天天爱跑步(线段树合并)

    线段树合并的话这个noip最难题就是个裸题了. 注意merge最后return x,以及如果需要区间查询的话这里还需要up,无数次死于这里. #include<iostream> #inc ...

  7. 【bzoj4719】[Noip2016]天天爱跑步 权值线段树合并

    题目描述 给出一棵n个点的树,以及m次操作,每次操作从起点向终点以每秒一条边的速度移动(初始时刻为0),最后对于每个点询问有多少次操作在经过该点的时刻为某值. 输入 第一行有两个整数N和M .其中N代 ...

  8. 洛谷P1600 天天爱跑步(线段树合并)

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

  9. 【LG1600】[NOIP2016]天天爱跑步

    [LG1600][NOIP2016]天天爱跑步 题面 洛谷 题解 考虑一条路径\(S\rightarrow T\)是如何给一个观测点\(x\)造成贡献的, 一种是从\(x\)的子树内出来,另外一种是从 ...

随机推荐

  1. Linux的Transparent Hugepage与关闭方法

    Transparent HugePages是在运行时动态分配内存的,而标准的HugePages是在系统启动时预先分配内存,并在系统运行时不再改变. 因为Transparent HugePages是在运 ...

  2. Linux chattr 命令详解

    常见命令参数 A:即Atime,告诉系统不要修改对这个文件的最后访问时间. S:即Sync,一旦应用程序对这个文件执行了写操作,使系统立刻把修改的结果写到磁盘. a:即Append Only,系统只允 ...

  3. 原生JavaScript可以干那些事情

    1.原生JavaScript实现字符串长度截取 function cutstr(str, len) { var temp; var icount = 0; var patrn = /[^\x00-\x ...

  4. app数据加密方法

    由于项目涉及到注册和登录,所以我了解一下现在app使用比较广泛的几种密码加密方法,可以当作参考,以下都是在python3.5的环境下使用这些算法的例子. 1.base64 Base64是一种基于64个 ...

  5. Chrome新发现

    昨晚写代码的时候惊喜的发现Chrome中能直接使用一些ES6的语法: let, const, 箭头函数等已经能直接使用. 酷酷的. 另外我的Chrome版本还是比较旧的,并不需要最新版本.

  6. JavaScript计算指定日期与当前日期的相差天数

    java中处理2个时间段相差几天很容易,直接调相关方法就行,不过js处理这类问题稍微有点不易,项目开发中也用到了,如下: 1 2 3 4 5 6 7 8 9 10 11 12 //计算两个日期天数差的 ...

  7. IDEA使用心得整理

    [演出模式] 我们可以使用[Presentation Mode],将IDEA弄到最大,可以让你只关注一个类里面的代码,进行毫无干扰的coding. 可以使用Alt+V快捷键,弹出View视图,然后选择 ...

  8. 用php代码统计数据库中符合条件的行数

    $sql1 = "select count(*) from t_user where age<17"; $data1 = mysql_query($sql1); $rows1 ...

  9. shell基础--cat命令的使用

    一.cat的常用用法 1.总结 2.实验 (1).非交互式编辑 [root@~_~ day5]# cat > cattest.sh <<STOP > hello > ST ...

  10. Ubuntu14.04更换阿里云源

    步骤很简单一共三步,如下所示: 第一.备份源文件(防止万一) sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak 第二.修改源文件(这里的源 ...