闲话
为了理清这道题目的思路,我是边写博客边做题的,qwq。
题目链接
题解
首先对变量进行声明
dep[i] 表示i号节点的深度,是到根节点的深度
w[i] 表示i号观测点观测的时间
dfn[i] 表示i号点的dfn序
sz[i] 表示i号点的子树大小
以上图为例,蓝色点表示一个玩家的起点和终点。
不妨先假设左边的点是起点,右边的点是终点,分别用\(s\)和\(t\)来表示
那么可能对答案产生贡献的点一定是在这个从\(s\)到\(t\)上的观测点。
记观测点为\(g\)。
对这个问题进行分类讨论
Case 1:观测点在起点到\(LCA\)的路径上
我们把这个情况记为观测点在\(A\)种路线上。
如果这个观测点能够观测到这个起点,那么一定满足以下的式子
\[dep[s]-dep[g]=w[g]\]
可以从图中观察到,\(dep[s]-dep[g]\)的值就是\(s\)到观测点\(g\)的时间长度。
为什么不需要\(±1\),是因为时间一开始是从\(0\)开始计数的。
换句话说,就是实际的通过时间就是通过边的数量或者是路径上经过点的数量\(-1\)。
对于现有式子进行变形。
\[dep[s]=dep[g]+w[g]\]
可以发现等式的右边是题目给定的定值。
那么问题就变成了对于每一个在\(A\)类路径上的观测点,起点的深度等于\(dep[g]+w[g]\)的个数。
暴力求解这个问题的复杂度差不多是\(n\times (dep[g]+w[g]-dep[s])\),明显过不了,(别忘了后面还有一个情况需要讨论)
我们可以把这个问题变一下,变成在以\(g\)为根的子树内,有多少个起点满足以上的性质。
转换成子树的问题就可以用树链剖分维护了。
先扯一个常识:在树上,一棵以\(u\)为根节点的子树的区间是\([dfn[u],sz[u]+dfn[u]-1]\)
问题转化成了:在区间\([dfn[u],sz[u]+dfn[u]-1]\)中有多少个深度等于\(dep[g]+w[g]\)的起点的个数。
可以对每一个深度建立一棵线段树。(动态开点,否则会MT飞掉)
那么问题就变成了在深度为\(dep[g]+w[g]\)的线段树中在区间\([dfn[u],sz[u]+dfn[u]-1]\)中有多少个起点。
如果暴力修改区间并且查询,修改的复杂度是\(O(dep\times n logn)\),来一条链就爆炸了。
考虑树上差分,其实这个东西我也想了很久,也不知道为什么可以差分,但是其实挺简单的。
为什么可以差分?
差分是什么?
差分只的前一个答案和后一个答案之间的差值。
在树上也就变成了祖先和儿子之间的关系。
先不要管线段树这个东西,会妨碍我们思考。
因为我们都知道,如果用差分计算一棵树上的答案,那么就是这颗树里面所有差分值全部\(+\)起来。
在这里因为路径\(s->lca\)的答案只有\(s\)这个起点有贡献。
模拟一下,如果是\(s\)的子树,很明显这个答案不会产生贡献。
如果是\(s->lca\)的链上,这个答案会对\(g\)贡献\(+1\)。
如果是lca以上的祖先,那么就在\(lca\)上打一个\(-1\)的标记。
抽象的概念就是:在这个深度上只有\(s->lca\)这一段区间的答案可以\(+1\)。
那么套一个线段树就可以了。
Case 2:观测点在\(LCA\)到终点的路径上
下面一半就简单了,图我就不画了。
得到产生贡献的式子
\[dep[s]+dep[g]-2\times dep[lca]=w[g]\]
前一半的式子其实就是求\(s->g\)的最短距离。
推导得到
\[dep[s]-2dep[lca]=w[g]-dep[g]\]
等式右边又是一个定值。
仿照上面的套路:对于深度建立线段树,打树上差分标记。
总的时间复杂度是:\(O(nlogn)\)
tips
- Case 2中的差值可能<0,需要整体右移,查询的时候也要整体右移,大小自己控制。
- 不要两次都把\(-1\)的标记都打在\(lca\)上,这样会减掉\(lca\)的答案,有一个标记打在\(lca\)的父亲上。
- 做完一遍后要请空数组。
Code
#include <bits/stdc++.h>
using namespace std;
namespace IOstream {
#define gc getchar
template <typename T> inline void read(T &x) {
x = 0; T fl = 1; char c = 0;
for (; c < '0' || c > '9'; c = gc()) if (c == '-') fl = -1;
for (; c >= '0' && c <= '9'; c = gc()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= fl;
}
template <typename T> inline void write(T x) {
if (x < 0) putchar('-'), x *= -1;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> inline void writeln(T x) { write(x); puts(""); }
template <typename T> inline void writesp(T x) { write(x); putchar(' '); }
#undef gc
} using namespace IOstream;
const int N = 3e6 + 5;
struct edge {
int to, nt;
} E[N << 1];
struct pl {
int s, t, lca;
} a[N];
int H[N];
int sz[N], fa[N], dep[N], son[N], top[N], dfn[N], rt[N];
int ecnt, n, m, __dfn = 0;
int w[N], ans[N];
void add_edge(int u, int v) {
E[++ ecnt] = (edge) {v, H[u]};
H[u] = ecnt;
}
void dfs1(int u, int ft = 0) {
fa[u] = ft; sz[u] = 1; dep[u] = dep[ft] + 1;
int maxson = -1;
for (int e = H[u]; e; e = E[e].nt) {
int v = E[e].to;
if (v == fa[u]) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > maxson) maxson = sz[v], son[u] = v;
}
}
void dfs2(int u, int tp = 1) {
top[u] = tp;
dfn[u] = ++ __dfn;
if (!son[u]) return;
dfs2(son[u], top[u]);
for (int e = H[u]; e; e = E[e].nt) {
int v = E[e].to;
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int LCA(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
namespace seg {
int tot;
struct node {
int lc, rc, s;
} tr[N * 10];
void clear() { tot = 0; memset(tr, 0, sizeof(tr)); }
void upd(int &nod, int l, int r, int k, int val) {
if (!k) return;
if (!nod) nod = ++ tot;
tr[nod].s += val;
if (l == r) return;
int mid = (l + r) >> 1;
if (k <= mid) upd(tr[nod].lc, l, mid, k, val);
else upd(tr[nod].rc, mid + 1, r, k, val);
}
int query(int nod, int l, int r, int ql, int qr) {
if (!nod) return 0;
if (ql <= l && r <= qr) return tr[nod].s;
int mid = (l + r) >> 1, res = 0;
if (ql <= mid) res += query(tr[nod].lc, l, mid, ql, qr);
if (qr > mid) res += query(tr[nod].rc, mid + 1, r, ql, qr);
return res;
}
}
signed main() {
read(n); read(m);
for (int i = 1, u, v; i < n; i ++) {
read(u); read(v);
add_edge(u, v); add_edge(v, u);
}
dep[0] = 0; dfs1(1);
dfs2(1);
for (int i = 1; i <= n; i ++) read(w[i]);
for (int i = 1; i <= m; i ++) {
read(a[i].s); read(a[i].t);
a[i].lca = LCA(a[i].s, a[i].t);
}
seg::clear(); memset(rt, 0, sizeof(rt));
for (int i = 1; i <= m; i ++) {
int root = dep[a[i].s];
seg::upd(rt[root], 1, n, dfn[a[i].s], 1);
seg::upd(rt[root], 1, n, dfn[fa[a[i].lca]], -1);
}
for (int i = 1; i <= n; i ++)
ans[i] += seg::query(rt[dep[i] + w[i]], 1, n, dfn[i], sz[i] + dfn[i] - 1);
seg::clear(); memset(rt, 0, sizeof(rt));
for (int i = 1; i <= m; i ++) {
int root = dep[a[i].s] - dep[a[i].lca] * 2 + 2 * n;
seg::upd(rt[root], 1, n, dfn[a[i].t], 1);
seg::upd(rt[root], 1, n, dfn[a[i].lca], -1);
}
for (int i = 1; i <= n; i ++)
ans[i] += seg::query(rt[w[i] - dep[i] + 2 * n], 1, n, dfn[i], sz[i] + dfn[i] - 1);
for (int i = 1; i <= n; i ++)
writesp(ans[i]);
return 0;
}