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;
}

  

 

posted @ 2014-07-28 15:34  Claris  阅读(281)  评论(0编辑  收藏  举报