• P3899 [湖南集训]谈笑风生 主席树


    #include<iostream>
    #include<string.h>
    #include<algorithm>
    #include<stdio.h>
    #include<math.h>
    #define rep(i,j,k) for(int i=j;i<=k;i++)
    #define per(i,j,k) for(int i=j;i>=k;i--)
    #define LL long long
    using namespace std;
    const int maxx = 3e5+6;
    struct node{
      int l,r;
      LL val;
    }tree[maxx<<5];
    int ver[maxx*2],Next[maxx*2],head[maxx];
    ///邻接表
    int deapth[maxx],Size[maxx],dfn[maxx],root[maxx];
    ///深度数组 包含自己在内的子节点数目 DFS序 主席树的根
    int tot,order,deep,cnt;
    ///边的序号 DFS序标记 最深深度
    void add(int x,int y){
       ver[++tot]=y;Next[tot]=head[x];head[x]=tot;
       ver[++tot]=x;Next[tot]=head[y];head[y]=tot;
    }
    void inserts(int l,int r,int pre,int &now,int pos,LL val){
         ///动态开点部分
         now=++cnt;
         ///把旧的节点的信息更新到新的节点上
         tree[now]=tree[pre];
         ///维护前缀和
         tree[now].val+=val;
         if (l==r){
            return;
         }
         int mid=(l+r)>>1;
         if (pos<=mid){
            inserts(l,mid,tree[pre].l,tree[now].l,pos,val);
         }else {
            inserts(mid+1,r,tree[pre].r,tree[now].r,pos,val);
         }
    }
    LL query(int L,int R,int l,int r,int ql,int qr){
         LL ans=0;
         ///如果在范围内,直接返回
         if (ql<=l && r<=qr){
            return tree[R].val-tree[L].val;
         }
         int mid=(l+r)>>1;
         if(ql<=mid)ans+=query(tree[L].l,tree[R].l,l,mid,ql,qr);
         if(qr>mid)ans+=query(tree[L].r,tree[R].r,mid+1,r,ql,qr);
          return ans;
    }
    void dfs1(int x,int fa){
         Size[x]=1;
         deapth[x]=deapth[fa]+1;
         ///更新最深是深度
         deep=max(deep,deapth[x]);
         for(int i=head[x];i;i=Next[i]){
            if(ver[i]==fa)continue;
            dfs1(ver[i],x);
            Size[x]+=Size[ver[i]];
         }
    }
    void dfs2(int x,int fa){
         dfn[x]+=++order;
         ///这里注意,由于我们需要按照树的变量顺序来建立序列,所以应该是root[order-1],root[order]
         ///size减1是为了减去自己
         inserts(1,deep,root[order-1],root[order],deapth[x],Size[x]-1);
         for (int i=head[x];i;i=Next[i]){
            if(ver[i]==fa)continue;
            dfs2(ver[i],x);
         }
    }
    int main(){
      int n,q;
      int uu,vv,l,r;
      while(~scanf("%d%d",&n,&q)){
          order=0;
          deep=0;
          cnt=0;
          memset(Next,0,sizeof(Next));
          memset(Size,0,sizeof(Size));
          memset(dfn,0,sizeof(dfn));
          memset(deapth,0,sizeof(deapth));
          rep(i,1,n-1){
             scanf("%d%d",&uu,&vv);
             add(uu,vv);
          }
          dfs1(1,0);
          dfs2(1,0);
          LL ans=0;
          int p,k;
          while(q--){
             scanf("%d%d",&p,&k);
             ans=0;
             ///当a点在b点的的下面,那么b所处于的位置,应该是其a的深度和k当中小的那个
             ///因为b只会在a的上面k个位置,并且如果a的深度太小的话,其深度可能达不到k个
             ///C的位置的可能一定是a的子节点
             ans+=(LL)(Size[p]-1)*min(k,deapth[p]-1);
             ///当b点在a的下端,那么c应该在b的子节点中
             ///所以我们要查询a也就是p的子节点中 所以第一个范围oot[dfn[p]-1],root[dfn[p]+Size[p]-1]
             ///查询其子节点中深度在deepth[p]+1到death[p]+k的节点中子节点的个数和
             ///因为我们构架线段树的时候,构建的是前缀和
             ans+=query(root[dfn[p]-1],root[dfn[p]+Size[p]-1],1,deep,deapth[p]+1,min(deapth[p]+k,deep));
             if (deapth[p]==deep)ans=0;
             printf("%lld
    ",ans);
          }
      }
      return 0;
    }
    有不懂欢迎咨询 QQ:1326487164(添加时记得备注)
  • 相关阅读:
    arcgis for flex 学习笔记(一)
    也说JS脚本加载控制
    数据验证随想(续)
    Oracle 脚本记录
    探讨:如何更快的赋值取值
    正则表达式技巧
    类似web风格的 Winform 分页控件
    数据验证随想
    [leetcode]Excel Sheet Column Title
    [leetcode]Merge k Sorted Lists
  • 原文地址:https://www.cnblogs.com/bluefly-hrbust/p/11343475.html
Copyright © 2020-2023  润新知