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;

}

  

 

你可能感兴趣的:(ZOJ)