解决树上问题的一个技巧:简单路径拆分成链,再分别考虑。
令某个人的路径为
P
P
P,其起点为
s
s
s,终点为
t
t
t,
l
c
a
(
s
,
t
)
=
g
lca(s,t)=g
lca(s,t)=g
一条路径拆分为向上和向下走,即:
从起点到
g
g
g的下面一个点(令其为
k
k
k,即
f
a
[
k
]
=
g
fa[k]=g
fa[k]=g,这是避免重复),和从
g
g
g到终点两个部分。
先考虑向上走的部分:
一个人从
s
s
s出发到
k
k
k,如果他能被某一个观察点
u
u
u看见,首先要满足这条路径经过了
u
u
u。
其次,要满足:
d
e
p
[
s
]
−
d
e
p
[
u
]
=
w
[
u
]
⇒
d
e
p
[
s
]
=
d
e
p
[
u
]
+
w
[
u
]
dep[s]-dep[u]=w[u] \Rightarrow dep[s]=dep[u]+w[u]
dep[s]−dep[u]=w[u]⇒dep[s]=dep[u]+w[u]
我们发现对于一个确定的点
u
u
u,
d
e
p
[
u
]
+
w
[
u
]
dep[u]+w[u]
dep[u]+w[u]是一个定值。
于是我们就要找到
u
u
u的子树中有多少个点
i
i
i作为起点,它的深度满足
d
e
p
[
i
]
=
d
e
p
[
u
]
+
w
[
u
]
dep[i]=dep[u]+w[u]
dep[i]=dep[u]+w[u],并且这条路径经过了
u
u
u。
对于前面的条件,可以用桶来记录。简单地说,就是每个点有 n n n个桶,记录其子树中深度为 1 1 1~ n n n的点分别有多少个,而一个点 u u u能看到的上升路径上的人就是 c n t [ d e p [ u ] + w [ u ] ] cnt[dep[u]+w[u]] cnt[dep[u]+w[u]]。
而由于有后面的限制,我们考虑换用差分来记录。对于经过型问题,差分是一个常用套路。即:在
s
s
s上打一个
c
n
t
[
d
e
p
[
s
]
]
cnt[dep[s]]
cnt[dep[s]]加一的标记,而在
f
a
[
k
]
fa[k]
fa[k]上打一个
c
n
t
[
d
e
p
[
s
]
]
cnt[dep[s]]
cnt[dep[s]]减一的标记。统计
u
u
u的子树和时,如果一条路径没有经过
u
u
u,它就不会有贡献,否则如果这条路径的起点在
u
u
u的子树内且这条路径经过了
u
u
u,它的贡献就是
1
1
1。
这样的话,对于一个
u
u
u,就只需要求出它的子树中
c
n
t
[
d
e
p
[
u
]
+
w
[
u
]
]
cnt[dep[u]+w[u]]
cnt[dep[u]+w[u]]的和即可。
那么向下走的路径也是类似的:
从
g
g
g出发到
t
t
t,如果能被
u
u
u看见,也首先要满足该路径经过了
u
u
u。
其次,有:
(
d
e
p
[
s
]
−
d
e
p
[
g
]
)
+
(
d
e
p
[
u
]
−
d
e
p
[
g
]
)
=
w
[
u
]
⇒
d
e
p
[
s
]
−
2
∗
d
e
p
[
g
]
=
w
[
u
]
−
d
e
p
[
u
]
(dep[s]-dep[g])+(dep[u]-dep[g])=w[u] \Rightarrow dep[s]-2*dep[g]=w[u]-dep[u]
(dep[s]−dep[g])+(dep[u]−dep[g])=w[u]⇒dep[s]−2∗dep[g]=w[u]−dep[u]
相当于是把上面一种情况的一个点的标记值从 d e p [ s ] dep[s] dep[s]变为了 d e p [ s ] − 2 ∗ d e p [ g ] dep[s]-2*dep[g] dep[s]−2∗dep[g],而所要统计子树和的值从 w [ u ] + d e p [ u ] w[u]+dep[u] w[u]+dep[u]变为了 w [ u ] − d e p [ u ] w[u]-dep[u] w[u]−dep[u]。
但是发现,如果这样每个点用 n n n个桶来记录,空间是装不下的。下面的处理方法比较巧妙,其实也就是子树和的一个常用转化:一个点的子树对应着 d f s dfs dfs序连续的一段区间,那么我们可以用 d f s dfs dfs完这个子树的总贡献减去 d f s dfs dfs该点前的总贡献 来得到这个子树的贡献。具体做法如下:
每个点用四个
v
e
c
t
o
r
vector
vector记录。分别是第一种情况的加、减标记,以及第二种情况的加、减标记。下面假设要求出点
u
u
u的
c
n
t
[
v
a
l
]
cnt[val]
cnt[val]的子树和:
我们记录一个全局的桶
s
u
m
[
1
sum[1
sum[1~
n
]
n]
n]。在搜索到每个点时,根据该点的所有标记值(加或减),修改
s
u
m
sum
sum的值。在
d
f
s
dfs
dfs每个点的儿子之前,先记录一下开始搜索之前的
s
u
m
[
v
a
l
]
sum[val]
sum[val]的值,令其为
S
U
M
SUM
SUM,然后搜索完若干儿子之后,再加上
u
u
u的贡献,会有一个新的
s
u
m
[
v
a
l
]
sum[val]
sum[val]的值,令其为
S
U
M
′
SUM'
SUM′。于是该点的子树中
c
n
t
[
v
a
l
]
cnt[val]
cnt[val]的和就是
S
U
M
′
−
S
U
M
SUM'-SUM
SUM′−SUM。
具体代码实现还是比较好理解的。要注意的是第二种情况有减法,可能小于
0
0
0,给所有值加上一个
n
n
n就可以了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10,Log=19;
int Head[maxn],V[maxn<<1],Next[maxn<<1],cnt=0;
int n,m,u,v,g,f[maxn][Log],dep[maxn],w[maxn],sum1[maxn],sum2[maxn<<1],ans[maxn];
vector<int> add1[maxn],dec1[maxn],add2[maxn],dec2[maxn];
inline void addedge(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x;
}
inline void dfs1(int u,int fa){
f[u][0]=fa,dep[u]=dep[fa]+1;
for(int i=1;i<Log;++i) f[u][i]=f[f[u][i-1]][i-1];
for(int i=Head[u];i;i=Next[i]) if(V[i]!=fa)
dfs1(V[i],u);
}
inline int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=Log-1;i>=0;--i) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
if(u==v) return u;
for(int i=Log-1;i>=0;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
return f[u][0];
}
inline void dfs2(int u,int f){
int d1=w[u]+dep[u],d2=w[u]-dep[u]+maxn,L1=sum1[d1],L2=sum2[d2];
for(int i=Head[u];i;i=Next[i]) if(V[i]!=f)
dfs2(V[i],u);
for(int i=0;i<add1[u].size();++i) ++sum1[add1[u][i]];
for(int i=0;i<dec1[u].size();++i) --sum1[dec1[u][i]];
ans[u]+=sum1[d1]-L1;
for(int i=0;i<add2[u].size();++i) ++sum2[add2[u][i]];
for(int i=0;i<dec2[u].size();++i) --sum2[dec2[u][i]];
ans[u]+=sum2[d2]-L2;
}
inline void print(int x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int main(){
//freopen("1578.in","r",stdin);
n=read(),m=read();
for(int i=1;i< n;++i)
u=read(),v=read(),addedge(u,v),addedge(v,u);
dfs1(1,0);for(int i=1;i<=n;++i) w[i]=read();
while(m--){
u=read(),v=read(),g=lca(u,v);
add1[u].push_back(dep[u]);
dec1[g].push_back(dep[u]);
add2[v].push_back(dep[u]-2*dep[g]+maxn);
dec2[f[g][0]].push_back(dep[u]-2*dep[g]+maxn);
}dfs2(1,0);
for(int i=1;i<=n;++i) print(ans[i]),putchar(' ');
return puts(""),0;
}