码迷,mamicode.com
首页 > 其他好文 > 详细

luogu P3899 [湖南集训]谈笑风生

时间:2018-10-04 10:49:33      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:i++   限制   sum   inline   遍历   reg   using   names   就是   

传送门

nmyzd,mgdhls,nbzdbnmgdnlql,a,whttxfs

对于一个点\(a\),点\(b\)只有可能是他的祖先或者在\(a\)子树里

如果点\(b\)\(a\)祖先,那么答案为a子树大小\(sz_a-1\)

否则,答案为\(sz_b-1\)

加上\(k\)的限制后,如果根节点1的深度\(de_1=1\)那么节点\(a\)的答案就是\(\sum_{b在a子树中,b\ne a,dist(a,b)\leq k}\ \ (sz_b-1)+\min(k,de_a-1)*(sz_a-1)\).后半段可以直接算,前半段的话,对每个节点开个线段树,保存子树中深度为某个值的\(\sum sz_a-1\),在遍历的时候合并线段树,查询的时候就查询区间\([de_a+1,de_a+k]\)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define eps (1e-7)

using namespace std;
const int N=300000+10;
const LL inf=(1ll<<50);
il LL rd()
{
  re LL x=0,w=1;re char ch=0;
  while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
  return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
il void add(int x,int y)
{
  ++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
  ++tot,to[tot]=x,nt[tot]=hd[y],hd[y]=tot;
}

LL s[N*30];
int ch[N*30][2],tt;

#define mid ((l+r)>>1)

il void psup(int o){s[o]=s[ch[o][0]]+s[ch[o][1]];}
void bui(int o,int l,int r,int lx,LL x)
{
  if(l==r){s[o]=x;return;}
  if(lx<=mid) bui(ch[o][0]=++tt,l,mid,lx,x);
  else bui(ch[o][1]=++tt,mid+1,r,lx,x);
  psup(o);
}
int merge(int o1,int o2)
{
  if(!o1) return o2;
  if(!o2||o1==o2) return o1;
  int o3=++tt;
  s[o3]=s[o1]+s[o2];
  ch[o3][0]=merge(ch[o1][0],ch[o2][0]);
  ch[o3][1]=merge(ch[o1][1],ch[o2][1]);
  return o3;
}
LL quer(int o,int l,int r,int ll,int rr)
{
  if(!o) return 0;
  if(ll<=l&&r<=rr) return s[o];
  LL an=0;
  if(ll<=mid) an+=quer(ch[o][0],l,mid,ll,rr);
  if(rr>mid) an+=quer(ch[o][1],mid+1,r,ll,rr);
  return an;
}
int n,m,rt[N];
int sz[N],de[N];
void dfs(int x,int ffa)
{
  sz[x]=1;
  for(int i=hd[x];i;i=nt[i])
    {
      int y=to[i];
      if(y==ffa) continue;
      de[y]=de[x]+1;
      dfs(y,x);
      sz[x]+=sz[y];
    }
  if(sz[x]>1)
    {
      bui(rt[x]=++tt,1,n,de[x],sz[x]-1);
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==ffa) continue;
          rt[x]=merge(rt[x],rt[y]);
        }
    }
}

int main()
{
  n=rd(),m=rd();
  for(int i=1;i<n;i++) add(rd(),rd());
  de[1]=1;
  dfs(1,0);
  while(m--)
    {
      int p=rd(),k=rd();
      printf("%lld\n",quer(rt[p],1,n,de[p]+1,de[p]+k)+1ll*(sz[p]-1)*min(de[p]-1,k));
    }
  return 0;
}

luogu P3899 [湖南集训]谈笑风生

标签:i++   限制   sum   inline   遍历   reg   using   names   就是   

原文地址:https://www.cnblogs.com/smyjr/p/9739329.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!