BZOJ3653 : 谈笑风生
设d[x]表示x到根的距离 size[x]表示x的子树大小(不含自己)
求出dfs序后按dfs序建主席树,线段树中区间[a,b]表示深度在[a,b]范围内的size[]的和
查询x,y的答案=size[x]*min(d[x],y)+dfs序在st[x]+1到en[x]之间且深度在d[x]+1到d[x]+k之间的size[]的和
时间复杂度$O((n+q)\log n)$
#include<cstdio> #define N 300010 #define M 5700000 typedef long long ll; int n,q,i,x,y,f[N],g[N],nxt[N<<1],v[N<<1],ed,size[N],d[N],st[N],en[N],dfn,seq[N],D,l[M],r[M],tot,root[N];ll val[M]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x){for(int i=g[seq[st[x]=++dfn]=x];i;i=nxt[i])if(v[i]!=f[x])f[v[i]]=x,d[v[i]]=d[x]+1,dfs(v[i]),size[x]+=size[v[i]]+1;en[x]=dfn;} int ins(int x,int a,int b,int c,int d){ int y=++tot; val[y]=val[x]+d; if(a==b)return y; int mid=(a+b)>>1; if(c<=mid)l[y]=ins(l[x],a,mid,c,d),r[y]=r[x];else l[y]=l[x],r[y]=ins(r[x],mid+1,b,c,d); return y; } ll ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return val[x]; int mid=(a+b)>>1;ll t=0; if(c<=mid&&l[x])t+=ask(l[x],a,mid,c,d); if(d>mid&&r[x])t+=ask(r[x],mid+1,b,c,d); return t; } int main(){ for(read(n),read(q),i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x); for(dfs(i=1);i<=n;i++)if(d[i]>D)D=d[i]; for(i=1;i<=n;i++)root[i]=ins(root[i-1],0,D,d[seq[i]],size[seq[i]]); while(q--)read(x),read(y),printf("%lld\n",(ll)size[x]*(d[x]<y?d[x]:y)+ask(root[en[x]],0,D,d[x],d[x]+y)-ask(root[st[x]],0,D,d[x],d[x]+y)); return 0; }