原题链接
要解决这题有一个很重要的思想,就是将跑步的路径拆开来,分成向上走的(S o LCA(S,T)),及向下走的(LCA(S,T) o T)((S)是路径起点,(T)是路径终点)。
然后对于两种路径单独统计贡献。
先只考虑向上走的路径(S o LCA(S,T))。
对于一个观测点(i)来说,只有满足(deep[S]-w[i]=deep[i])((deep)表示点在树中的深度)的路径才能对它产生贡献。移项得(deep[S]=deep[i]+w[i]),于是我们可以对树进行(dfs),并统计。
用一个桶(up)来统计当前贡献。
当搜到第(i)个点时(显然它的子树都已经统计完了),先在桶里加入从该点出发的路径,即(up[deep[i]]+=SUM\_S[i])。
然后计算能对该点产生贡献的路径数,即(ans[i]+=up[deep[i]+w[i]])(注意(deep[i]+w[i])是可能越界的)
在退出该点时,从桶里减去向上走的路径中到达该点就不往上走的路径,即该点就是(LCA(S,T)),向上走路径的终点。
注意因为一棵树里有很多同一深度的点,我们统计的不一定是同一棵子树下的,所以我们可以在进入该点时记录下原始的(up[deep[i]+w[i]])值,在将子树搜索完后,用新的值减去原始值即是这棵子树所产生的贡献。
而对于向下走的路径(LCA(S,T) o T)的统计方法类似,只有满足(deep[T]-deep[i]=length-w[i])((length)表示路径长度)的路径才能对它产生贡献。移项得(deep[T]-length=deep[i]-w[i]),于是我们可以相似的用桶在(dfs)中统计。
当搜到第(i)个点时,一样先在桶里加入终点为该点的路径(因为(dfs)统计是向上统计,所以要加入终点),然后统计能对该点产生贡献的路径数。
在退出该点时,从桶里减去向下走路径中起点为该点的路径,即该点是(LCA(S,T))。
注意(deep[i]-w[i])及(deep[T]-length)可能是负数,所以需要加上一个数,平移数组。
最后将重复计算的(LCA)的贡献减(1)即可。
(LCA)部分可用倍增,或是(tarjan),这里我是用的倍增。
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int N = 3e5 + 10;
const int M = N << 1;
const int K = 3e5;
struct ts {
int x, y, L, LCA;
};
ts a[N];
int fi[N], di[M], ne[M], f[N][20], de[N], an[N], ss[N], up[N], dn[M], w[N], mdp, l, gn;
vector<int>ule[N], dmo[N], dle[N];
inline int re()
{
int x = 0;
char c = getchar();
bool p = 0;
for (; c < '0' || c > '9'; c = getchar())
p |= c == '-';
for (; c >= '0' && c <= '9'; c = getchar())
x = x * 10 + c - '0';
return p ? -x : x;
}
inline void add(int x, int y)
{
di[++l] = y;
ne[l] = fi[x];
fi[x] = l;
}
inline int maxn(int x, int y)
{
return x > y ? x : y;
}
inline void sw(int &x, int &y)
{
int z = x;
x = y;
y = z;
}
void dfs(int x)
{
int i, y;
mdp = maxn(mdp, de[x]);
for (i = 1; i <= gn; i++)
f[x][i] = f[f[x][i - 1]][i - 1];
for (i = fi[x]; i; i = ne[i])
if ((y = di[i]) ^ f[x][0])
{
de[y] = de[x] + 1;
f[y][0] = x;
dfs(y);
}
}
int lca(int x, int y)
{
int i;
if (de[x] > de[y])
sw(x, y);
for (i = gn; ~i; i--)
if (de[f[y][i]] >= de[x])
y = f[y][i];
if (!(x ^ y))
return x;
for (i = gn; ~i; i--)
if (f[x][i] ^ f[y][i])
{
x = f[x][i];
y = f[y][i];
}
return f[x][0];
}
void dfs_up(int x)
{
int i, y, la, nw = w[x] + de[x], si = ule[x].size();
if (nw <= mdp)
la = up[nw];
for (i = fi[x]; i; i = ne[i])
if ((y = di[i]) ^ f[x][0])
dfs_up(y);
up[de[x]] += ss[x];
if (nw <= mdp)
an[x] = up[nw] - la;
for (i = 0; i < si; i++)
up[ule[x][i]]--;
}
void dfs_down(int x)
{
int i, y, la, nw = de[x] - w[x], si_1 = dmo[x].size(), si_2 = dle[x].size();
la = dn[nw + K];
for (i = fi[x]; i; i = ne[i])
if ((y = di[i]) ^ f[x][0])
dfs_down(y);
for (i = 0; i < si_1; i++)
dn[dmo[x][i] + K]++;
an[x] += dn[nw + K] - la;
for (i = 0; i < si_2; i++)
dn[dle[x][i] + K]--;
}
int main()
{
int i, n, m, x, y;
n = re();
m = re();
gn = log2(n);
for (i = 1; i < n; i++)
{
x = re();
y = re();
add(x, y);
add(y, x);
}
de[1] = 1;
dfs(1);
for (i = 1; i <= n; i++)
w[i] = re();
for (i = 1; i <= m; i++)
{
a[i].x = re();
a[i].y = re();
ss[a[i].x]++;
a[i].LCA = lca(a[i].x, a[i].y);
a[i].L = de[a[i].x] + de[a[i].y] - (de[a[i].LCA] << 1);
ule[a[i].LCA].push_back(de[a[i].x]);
dmo[a[i].y].push_back(de[a[i].y] - a[i].L);
dle[a[i].LCA].push_back(de[a[i].y] - a[i].L);
}
dfs_up(1);
dfs_down(1);
for (i = 1; i <= m; i++)
if (!((de[a[i].x] - w[a[i].LCA]) ^ de[a[i].LCA]))
an[a[i].LCA]--;
for (i = 1; i <= n; i++)
printf("%d ", an[i]);
return 0;
}