@wsndy-xx
2018-05-05T03:24:57.000000Z
字数 3905
阅读 1545
wsndy
18.05.04
题解小c同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。
这个游戏的地图可以看作一一棵包含 个结点和 条边的树, 每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 到 的连续正整数。
现在有 个玩家,第 个玩家的起点为 ,终点为 。每天打卡任务开始时,所有玩家在第 秒同时从自己的起点出发, 以每秒跑一条边的速度, 不间断地沿着最短路径向着自己的终点跑去, 跑到终点后该玩家就算完成了打卡任务。 (由于地图是一棵树, 所以每个人的路径是唯一的)
小C想知道游戏的活跃度, 所以在每个结点上都放置了一个观察员。 在结点 的观察员会选择在第 秒观察玩家, 一个玩家能被这个观察员观察到当且仅当该玩家在第 秒也理到达了结点 。小C想知道每个观察员会观察到多少人?
注意: 我们认为一个玩家到达自己的终点后该玩家就会结束游戏, 他不能等待一段时间后再被观察员观察到。 即对于把结点 作为终点的玩家: 若他在第 秒前到达终点,则在结点 的观察员不能观察到该玩家;若他正好在第 秒到达终点,则在结点 的观察员可以观察到这个玩家。
存在一棵树,树上的每个节点有一个观察时间,如果一条路径从起点到终点的过程中经过这个点的时间(从起点出发的时间为 , 每经过一条边花费时间为 )与这个点的观察时间相同,那么这个点就会观察到这条路径,询问 条路径后每个节点观察到的路径数量。
lca - 对路径划分
树链剖分 - 求lca,化树成链
树上差分 - 统计
线段树 (动态开节点) - 统计
deep[a] 节点a的深度
wat[a] 节点a出现观察员的时间 //watch
s 一条路径的起点,代码中用Askl[]表示
t 一条路径的终点,代码中用Askr[]表示
lca(a,b) a,b 的lca
对于树上的问题往往将树上的一段路径拆成 和 两条路径来处理
首先将路径拆成 和 两段路径
对于 是向上的路径,如果该路径能被 节点观察到,我们就可以得到
同理,对于 的这段路径
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int N = 3e5 + 10;#define gc getchar()int n, m, wat[N];int Askl[N], Askr[N], Lca[N];int now = 1, head[N];struct Node {int v, nxt;} G[N << 1];inline int read() {int x = 0; char c = gc;while(c < '0' || c > '9') c = gc;while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = gc;return x;}inline void Add(int u, int v) {G[now].v = v; G[now].nxt = head[u]; head[u] = now ++;}int fa[N], deep[N], top[N], size[N], son[N], lst[N], rst[N], tree[N], Spjs;void Dfs_1(int u, int f_, int dep) {fa[u] = f_; deep[u] = dep; size[u] = 1;for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(v != f_) {Dfs_1(v, u, dep + 1);size[u] += size[v];if(size[son[u]] < size[v]) son[u] = v;}}}void Dfs_2(int u, int tp) {top[u] = tp;lst[u] = ++ Spjs;tree[u] = Spjs;if(! son[u]) {rst[u] = Spjs; return ;}Dfs_2(son[u], tp);for(int i = head[u]; ~ i; i = G[i].nxt) {int v = G[i].v;if(v != fa[u] && v != son[u]) Dfs_2(v, v);}rst[u] = Spjs;}inline int Ask_Lca(int x, int y) {int tp1 = top[x], tp2 = top[y];while(tp1 != tp2) {if(deep[tp1] < deep[tp2]) swap(x, y), swap(tp1, tp2);x = fa[tp1];tp1 = top[x];}return deep[x] < deep[y] ? x : y;}int root[N * 3], lson[N * 25], rson[N * 25], W[N * 25], tot, cnt;void Build_G(int l, int r, int & jd, int x, int yj) {if(!x) return ;if(!jd) jd = ++ tot;W[jd] += yj;if(l == r) return ;int mid = (l + r) >> 1;if(x <= mid) Build_G(l, mid, lson[jd], x, yj);else Build_G(mid + 1, r, rson[jd], x, yj);}int Sec_A(int jd, int l, int r, int x, int y) {if(! jd) return 0;if(x <= l && r <= y) return W[jd];int mid = (l + r) >> 1;if(y <= mid) return Sec_A(lson[jd], l, mid, x, y);else if(x > mid) return Sec_A(rson[jd], mid + 1, r, x, y);else return Sec_A(lson[jd], l, mid, x, y) + Sec_A(rson[jd], mid + 1, r, x, y);}void Clear() {tot = 0;memset(lson, 0, sizeof lson);memset(rson, 0, sizeof rson);memset(W, 0, sizeof W);memset(root, 0, sizeof root);}int Answer[N];int main() {n = read(); m = read();for(int i = 1; i <= n; i ++) head[i] = -1;for(int i = 1; i <= n - 1; i ++) {int u = read(), v = read();Add(u, v); Add(v, u);}for(int i = 1; i <= n; i ++) wat[i] = read();for(int i = 1; i <= m; i ++) Askl[i] = read(), Askr[i] = read();Dfs_1(1, 0, 0);Dfs_2(1, 1);for(int i = 1; i <= m; i ++) Lca[i] = Ask_Lca(Askl[i], Askr[i]);int dep;for(int i = 1; i <= m; i ++) {dep = deep[Askl[i]];Build_G(1, n, root[dep], tree[Askl[i]], 1);Build_G(1, n, root[dep], tree[fa[Lca[i]]], -1);}for(int i = 1; i <= n; i ++)Answer[i] = Sec_A(root[deep[i] + wat[i]], 1, n, lst[i], rst[i]);Clear();for(int i = 1; i <= m; i ++) {dep = deep[Askl[i]] - deep[Lca[i]] * 2 + n * 2;Build_G(1, n, root[dep], tree[Askr[i]], 1);Build_G(1, n, root[dep], tree[Lca[i]], -1);}for(int i = 1; i <= n; i ++)Answer[i] += Sec_A(root[wat[i] - deep[i] + n * 2], 1, n, lst[i], rst[i]);for(int i = 1; i <= n; i ++)cout << Answer[i] << " ";return 0;}