zoukankan      html  css  js  c++  java
  • luogu P3899 [湖南集训]谈笑风生

    传送门

    nmyzd,mgdhls,bnmbzdgdnlql,a,wgttxfs

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

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

    否则,答案为(sz_b-1)

    加上(k)的限制后,如果根节点1的深度(de_1=1)那么节点(a)的答案就是(sum_{b在a子树中,b e 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
    ",quer(rt[p],1,n,de[p]+1,de[p]+k)+1ll*(sz[p]-1)*min(de[p]-1,k));
        }
      return 0;
    }
    
    

    upd 12.6:

    其实这题可以也可以用长链剖分,因为我们可以设(f_{x,j})为点(x)子树内深度为(de_x+j)的所有点的((sz_x-1))之和,转移是(f_{x,j}=sum_{y=son_x}f_{y,j-1}),这个可以长链剖分后优化,直接继承重儿子状态,暴力转移轻儿子状态

    处理询问和上面类似,只不过这里要把(f_{x,j})搞成后缀和形式

    // 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;
    }
    int n,m,fa[N],sz[N],de[N],dpt[N],hson[N];
    int qk[N],qnt[N],qid[N],qhd[N],qt=1;
    il void qaq(int x,int k,int id){++qt,qk[qt]=k,qnt[qt]=qhd[x],qid[qt]=id,qhd[x]=qt;}
    LL an[N];
    void dfs1(int x)
    {
      sz[x]=1;
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==fa[x]) continue;
          fa[y]=x,dpt[y]=de[y]=de[x]+1,dfs1(y),sz[x]+=sz[y];
          if(dpt[hson[x]]<dpt[y]) hson[x]=y;
          dpt[x]=max(dpt[x],dpt[y]);
        }
    }
    LL *f[N],rbq[N<<1],*px=rbq+1;
    void dd(int x)
    {
      if(hson[x]) f[hson[x]]=f[x]+1,dd(hson[x]);
      for(int i=hd[x];i;i=nt[i])
        {
          int y=to[i];
          if(y==fa[x]||y==hson[x]) continue;
          f[y]=px,px+=dpt[y]-de[x]+1,dd(y);
          for(int i=dpt[y]-de[x]-1;i>=0;--i) f[x][i+1]+=f[y][i];
        }
      for(int i=qhd[x];i;i=qnt[i])
        {
          int k=qk[i],id=qid[i];
          an[id]=f[x][1]+1ll*(sz[x]-1)*min(de[x]-1,k);
          if(k+1<=dpt[x]-de[x]) an[id]-=f[x][k+1];
        }
      f[x][0]=f[x][1]+sz[x]-1;
    }
    
    int main()
    {
      n=rd(),m=rd();
      for(int i=1;i<n;i++) add(rd(),rd());
      de[1]=1;
      dfs1(1);
      for(int i=1;i<=m;++i)
        {
          int p=rd(),k=rd();
          qaq(p,k,i);
        }
      f[1]=px,px+=dpt[1];
      dd(1);
      for(int i=1;i<=m;++i) printf("%lld
    ",an[i]);
      return 0;
    }
    

    运行结果(O2):线段树合并 2022ms,长链剖分 942ms

    后者不知道比前者高到哪去了,虽然不能和最优解谈笑风生

  • 相关阅读:
    初涉数组
    声明
    简述java程序中的main方法
    概述java语言
    1.3 linux基础(三)
    linux基础之-screen命令
    1.2 linux基础(二)
    1.1 Linux基础(一)
    实验7-1-13 装箱问题
    实验7-1-12 组个最小数
  • 原文地址:https://www.cnblogs.com/smyjr/p/9739329.html
Copyright © 2011-2022 走看看