先跑出dfs序。。然后对于询问p,k ans=min(k,dep[p])*s[p]+∑s[y]
y是p子树上的点且满足距离限制。
只要对dfs序建可持久化线段树就可以快速求出∑s[y]
(权当复习可持久化线段树了。。。询问的时候因为这是一棵可持久化线段树,所以访问到的节点如果是空就要退出来不然就要T。。
#include<cstring> #include<iostream> #include<cstdio> #include<algorithm> #include<queue> #define rep(i,l,r) for (int i=l;i<=r;i++) #define down(i,l,r) for (int i=l;i>=r;i--) #define clr(x,y) memset(x,y,sizeof(x)) #define maxn 300500 #define inf 0x7fffffff #define ll long long #define mm 100000007 using namespace std; struct data{int obj,pre; }e[maxn*2]; int head[maxn],l[maxn],r[maxn]; int s[maxn],num[maxn],dep[maxn]; int root[maxn],lc[maxn*20],rc[maxn*20]; ll sum[maxn*20]; int tot,cnt,n,mx,idx,m; int read(){ ll x=0,f=1; char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)){x=x*10+ch-'0'; ch=getchar();} return x*f; } void insert(int x,int y){ e[++tot].obj=y; e[tot].pre=head[x]; head[x]=tot; } void dfs(int u,int f){ l[u]=++idx; num[idx]=u; for (int j=head[u];j;j=e[j].pre){ int v=e[j].obj; if (v==f) continue; dep[v]=dep[u]+1; mx=max(mx,dep[v]); dfs(v,u); s[u]+=s[v]+1; } r[u]=idx; } void add(int l,int r,int x,int &y,int val,int pos){ y=++cnt; sum[y]=sum[x]+1LL*val; if (l==r) return; lc[y]=lc[x]; rc[y]=rc[x]; int mid=(l+r)/2; if (pos<=mid) add(l,mid,lc[x],lc[y],val,pos); else add(mid+1,r,rc[x],rc[y],val,pos); } ll ask(int l,int r,int x,int L,int R){ if (!x) return 0; if (L<=l&&r<=R) return sum[x]; int mid=(l+r)/2; if (R<=mid) return ask(l,mid,lc[x],L,R); else if (L>mid) return ask(mid+1,r,rc[x],L,R); else return ask(l,mid,lc[x],L,mid)+ask(mid+1,r,rc[x],mid+1,R); } int main(){ n=read(); m=read(); rep(i,1,n-1){ int x=read(),y=read(); insert(x,y); insert(y,x); } dfs(1,0); rep(i,1,n) add(0,mx,root[i-1],root[i],s[num[i]],dep[num[i]]); rep(i,1,m){ int x=read(),y=read(); ll ans=1LL*min(y,dep[x])*s[x]; int L=dep[x]+1,R=min(mx,dep[x]+y); ans+=ask(0,mx,root[r[x]],L,R); ans-=ask(0,mx,root[l[x]-1],L,R); printf("%lld\n",ans); } return 0; }