设siz[i]表示i的子树大小-1。
询问中b在a上方的便于统计。
对于b在a下方的情况,贡献为距a距离在K以内的节点的siz之和。
按dfs序建立可持久化线段树,线段树的下标是深度。
相当于把每个点(带权)映射到了平面上,然后求一个矩形内的点权之和。
#include<cstdio> #include<algorithm> using namespace std; #define N 300001 typedef long long ll; struct Node{int lc,rc;ll v;}T[N*24]; int root[N]; int en,v[N<<1],first[N],next[N<<1],siz[N],dep[N],Ls[N],e,Rs[N]; int ma_siz[N],ma_dep[N]; void AddEdge(int U,int V) { v[++en]=V; next[en]=first[U]; first[U]=en; } void dfs(int U,int Fa) { siz[U]=1; Ls[U]=++e; dep[U]=dep[Fa]+1; for(int i=first[U];i;i=next[i]) if(v[i]!=Fa) { dfs(v[i],U); siz[U]+=siz[v[i]]; } Rs[U]=e; } int n,m; void Update(int pre,int cur,int p,int v,int l,int r) { if(l==r) { T[cur].v=T[pre].v+(ll)v; return; } int m=(l+r>>1); if(p<=m) { T[cur].lc=++e; T[cur].rc=T[pre].rc; Update(T[pre].lc,T[cur].lc,p,v,l,m); } else { T[cur].rc=++e; T[cur].lc=T[pre].lc; Update(T[pre].rc,T[cur].rc,p,v,m+1,r); } T[cur].v=T[T[cur].lc].v+T[T[cur].rc].v; } ll Query(int pre,int cur,int ql,int qr,int l,int r) { if(ql<=l&&r<=qr) return T[cur].v-T[pre].v; int m=(l+r>>1); ll res=0; if(ql<=m) res+=Query(T[pre].lc,T[cur].lc,ql,qr,l,m); if(m<qr) res+=Query(T[pre].rc,T[cur].rc,ql,qr,m+1,r); return res; } int main() { int x,y; scanf("%d%d",&n,&m); for(int i=1;i<n;++i) { scanf("%d%d",&x,&y); AddEdge(x,y); AddEdge(y,x); } dfs(1,0); e=0; for(int i=1;i<=n;++i) { --siz[i]; ma_siz[Ls[i]]=siz[i]; ma_dep[Ls[i]]=dep[i]; } for(int i=1;i<=n;++i) { root[i]=++e; Update(root[i-1],root[i],ma_dep[i],ma_siz[i],1,n); } for(;m;--m) { scanf("%d%d",&x,&y); printf("%lld ",(ll)min(dep[x]-1,y)*(ll)siz[x]+ Query(root[Ls[x]],root[Rs[x]],dep[x]+1,min(dep[x]+y,n),1,n)); } return 0; }