终于把这个坑填了!
思路还是很值得学习的,这篇博客写的很详细。
重要的是拆成两种路径,推出这两个公式:
up:T[i]+dep[i]=dep[s];
down:dep[v]-dis(u,v)=dep[i]-T[i]
两次dfs分别对应up和down两种情况,dfs里对于每一个i,统计其子树内满足等式的点s的个数(用捅保存),这样就对ans[i]做出贡献。
每次x处理完后要删去桶中以i为LCA的路径的起点深度桶的值。
因为当我们遍历完i节点的孩子时,对于以i节点为LCA的路径来说。
这条路径上的信息对i的祖先节点是不会有影响的。(因为lca就是其能走到的最高的点了)
所以要将其删去。
注意:(摘自洛谷题解)
还有就是down里面下标可能为负,全部加一个N即可。
#include<bits/stdc++.h> #define N 300002 using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } struct EDGE{ int nextt,to; }w[N*2]; struct play{ int s,t,lca,len; }p[N]; int tot=0; int head[N],f[N][21],dep[N],T[N],tong[N],tong2[N],num[N],ans[N]; vector<int>a[N],b[N],c[N]; void add(int a,int b) { tot++; w[tot].nextt=head[a]; w[tot].to=b; head[a]=tot; } void dfs(int x) { for(int i=head[x];i;i=w[i].nextt) { int v=w[i].to; if(v==f[x][0])continue; dep[v]=dep[x]+1; f[v][0]=x; for(int i=1;i<=20;++i)f[v][i]=f[f[v][i-1]][i-1]; dfs(v); } } int LCA(int x,int y) { if(dep[x]<dep[y])swap(x,y); for(int i=20;i>=0;--i)if(dep[f[x][i]]>=dep[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]; } void dfs_up(int x)//T[i]+dep[i]=dep[s]; {//x作为lca的贡献//只要符合dep[i]+T[i]的是玩家起点的点,就能对i点产生贡献 int now=dep[x]+T[x],last=tong[now];//now即s,深度大于x,已经更新过了 for(int i=head[x];i;i=w[i].nextt) { int v=w[i].to; if(v==f[x][0])continue; dfs_up(v); } tong[dep[x]]+=num[x];//为可以成为它的lca的点做准备 ans[x]+=tong[now]-last;//跨子树的情况所以减去last ans[i]加上的其实就是i的子树对i的贡献 for(int i=0;i<a[x].size();++i)tong[dep[a[x][i]]]--;//x退出,作为lca的贡献减去 } void dfs_down(int x)//dep[v]-dis(u,v)=dep[i]-T[i] { int now=dep[x]-T[x],last=tong2[now+N];//now可能为负,处理时都加上一个N for(int i=head[x];i;i=w[i].nextt) { int v=w[i].to; if(v==f[x][0])continue; dfs_down(v); } for(int i=0;i<b[x].size();++i)tong2[b[x][i]+N]++; ans[x]+=tong2[now+N]-last; for(int i=0;i<c[x].size();++i)tong2[c[x][i]+N]--; } int main() { int n=read(),m=read(); for(int i=1;i<n;++i) { int a=read(),b=read(); add(a,b);add(b,a); } dep[1]=1; dfs(1); for(int i=1;i<=n;++i) T[i]=read(); for(int i=1;i<=m;++i) { p[i].s=read();p[i].t=read(); num[p[i].s]++;//这里可以直接用num保存s的个数,而down不行是因为每一个的dis是不一样的 p[i].lca=LCA(p[i].s,p[i].t); p[i].len=dep[p[i].s]+dep[p[i].t]-2*dep[p[i].lca]; a[p[i].lca].push_back(p[i].s); b[p[i].t].push_back(dep[p[i].t]-p[i].len);//num和b的实质其实是一样的 c[p[i].lca].push_back(dep[p[i].t]-p[i].len); } dfs_up(1); dfs_down(1); for(int i=1;i<=m;++i) if(dep[p[i].s]-dep[p[i].lca]==T[p[i].lca])ans[p[i].lca]--;//重复算了,位于lca的位置 for(int i=1;i<=n;++i)printf("%d ",ans[i]); }