题意:
有nnn个地方,被n−1n-1n−1条道路连接,有mmm个人同时开始跑,起点和终点分别为ui,viu_{i},v_{i}ui,vi,每个地方有一个观察员,仅会在tit_{i}ti时刻观察到达iii地方的人,问每个观察员能看到多少个人?
Solution:
显然模拟跑步过程复杂度不够,每次模拟路程长度≤n\leq n≤n,复杂度O(nm)O(nm)O(nm)。换个角度,由于每个跑步者的路径(x→y)(x\rightarrow y)(x→y)一定经过lca(x,y)lca(x,y)lca(x,y),她只会给lca(x,y)lca(x,y)lca(x,y)这棵子树的观察员带来贡献,由于这是个单向过程,我们考虑拆分路径为(x→lca→y)(x\rightarrow lca\rightarrow y)(x→lca→y),不妨说分为上行和下行两段。
先考虑上行能给uuu点带来贡献的点有什么条件,显然,从起点出发,到达的时候恰好被看到,即花费了tut_{u}tu到达uuu,又由于是上行,于是只要深度为depth[u]+t[u]depth[u]+t[u]depth[u]+t[u]的点,并且位于uuu的子树内,就是被看到的上行点。
再考虑下行能给uuu点带来贡献的点有什么条件,同理,从起点出发tut_{u}tu秒被看到,那么他们的总路径长就是(x→u→y)(x\rightarrow u \rightarrow y)(x→u→y),有如下等式
dis(x,y)=dis(x,u)+dis(u,y) dis(x,y)=dis(x,u)+dis(u,y) dis(x,y)=dis(x,u)+dis(u,y)
其中dis(x,u)dis(x,u)dis(x,u)恰好是时间tut_{u}tu,而由于下行,u,yu,yu,y位于同一条链,于是
dis(x,y)=tu+depthy−depthu dis(x,y)=t_{u}+depth_{y}-depth_{u} dis(x,y)=tu+depthy−depthu
即
depthy−dis(x,y)=depthu−tu depth_{y}-dis(x,y)=depth_{u}-t_{u} depthy−dis(x,y)=depthu−tu
终点yyy满足以上等式的即是下行给uuu带来贡献的点。
两部分答案都可以用桶解决,需要注意的是
(1)由于答案限制在子树内,我们需要获得单一子树的桶,不能暴力统计,需要启发式合并
(2)由于答案限制在子树内,需要提前统计以uuu为lcalcalca的点,在结束uuu的递归的时候把以uuu为lcalcalca的点的贡献全删了
(3)第二部分的等式一边可能是负,于是需要偏移数组
(4)两部分答案的计算顺序不同,第一部分顺序为:加上当前点的贡献,计算答案,减去以该点为lcalcalca的贡献,第二部分由于第一部分已经统计过以uuu点为lcalcalca的路径,所以顺序为:加上当前点的贡献,减去以该点为lcalcalca的贡献,计算答案。
// #include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
using ll=long long;
const int N=300005,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=998244353;
struct node
{
int first,second;
}a[N];
struct way
{
int to,next;
}edge[N<<1];
int cntt,head[N];
void add(int u,int v)
{
edge[++cntt].to=v;
edge[cntt].next=head[u];
head[u]=cntt;
}
int n,m,t[N],tot[N],f[N][21],depth[N],cnt[N<<2];
int ans[N],val[N],sum[N],son[N];
vector<vector<int>>from(N),lcaof(N);
void dfs1(int u,int fa)
{
// printf("u=%d\n",u);
f[u][0]=fa; depth[u]=depth[fa]+(sum[u]=1);
for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
dfs1(v,u);
sum[u]+=sum[v];
if(sum[v]>sum[son[u]]) son[u]=v;
}
}
int lca(int x,int y)
{
if(depth[x]<depth[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(depth[x]-(1<<i)>=depth[y]) x=f[x][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int dis(int x,int y)
{
int tmp=lca(x,y);
return depth[x]+depth[y]-2*depth[tmp];
}
void gls2(int u,int hson,int fa,int k)
{
cnt[depth[u]]+=val[u]*k;
for(auto v:lcaof[u]) cnt[depth[v]]-=k;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if((k==1&&v==hson)||v==fa) continue;
gls2(v,hson,u,k);
}
}
void dfs2(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa||v==son[u]) continue;
dfs2(v,u);
}
if(son[u]) dfs2(son[u],u);
gls2(u,son[u],fa,1);
ans[u]=cnt[depth[u]+t[u]]+tot[u];
if(son[fa]!=u) gls2(u,son[u],fa,-1);
}
void gls3(int u,int hson,int fa,int k)
{
for(auto v:lcaof[u]) cnt[depth[u]-dis(u,v)+N]-=k;
for(auto v:from[u]) cnt[depth[u]-dis(u,v)+N]+=k;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if((k==1&&v==hson)||v==fa) continue;
gls3(v,hson,u,k);
}
}
void dfs3(int u,int fa)
{
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(v==fa||v==son[u]) continue;
dfs3(v,u);
}
if(son[u]) dfs3(son[u],u);
gls3(u,son[u],fa,1);
ans[u]+=cnt[depth[u]-t[u]+N];
if(son[fa]!=u) gls3(u,son[u],fa,-1);
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int u,v; scanf("%d%d",&u,&v);
add(u,v); add(v,u);
}
for(int i=1;i<=n;i++) scanf("%d",&t[i]);
for(int i=1;i<=m;i++)
{
auto& [u,v]=a[i];
scanf("%d%d",&u,&v);
from[v].push_back(u);
}
dfs1(1,0);
for(int i=1;i<=m;i++)
{
auto& [u,v]=a[i];
int tmp=lca(u,v);
if(depth[u]==depth[tmp]+t[tmp]) tot[tmp]++;//记录有多少个路线以u为lca,并且对这个点产生贡献
//gls()统计完轻儿子时实际上已经清空了以这棵子树的点为lca的清空了,此时以u的需要手动求一下
lcaof[tmp].push_back(u);//记录以tmp为lca的点
val[u]++;//保存有多少个人以u为起点
}
dfs2(1,0);
for(int i=1;i<=n;i++) lcaof[i].clear();
memset(tot,0,sizeof(tot));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=m;i++)
{
auto& [u,v]=a[i];
lcaof[lca(u,v)].push_back(u);
}
dfs3(1,0);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}