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

    题意:

      设 T 为一棵有根树,我们做如下的定义:

      • 设 a 和 b 为 T 中的两个不同节点。如果 a 是 b 的祖先,那么称“a 比 b 不知道高明到哪里去了”。

      • 设 a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定常数 x,那么称“a 与 b 谈笑风生”。

    给定一棵 n 个节点的有根树 T,节点的编号为 1 ∼ n,根节点为 1 号节点。你需要回答 q 个询问,询问给定两个整数 p 和 k,问有多少个有序三元组 (a; b; c) 满足:

      1. a、 b 和 c 为 T 中三个不同的点,且 a 为 p 号节点;

      2. a 和 b 都比 c 不知道高明到哪里去了;

      3. a 和 b 谈笑风生。这里谈笑风生中的常数为给定的 k。

    分析:

      这道题目,也许是出题人有意地把题目描述复杂化了。实际上这并不是一道难题,甚至达不到近年来省选的难度。

      但是这确实是一道值得交流的题目,我见过的这道题就有不下五种做法,有在线的,有离线的,我用的是在线的主席树维护dfs序的方法。我开始以为这道题让求的是这样的有序三元组的数目,感觉十分不可做,后来发现,a点是已经固定的。

      我们不考虑什么“高明到哪里去了”,三元组的要求是说,给出a,求点a和点b都在c到根的路径上,也就是都是c的祖先,并且a和b距离不超过k的(a,b)数目。

      因为a和b在c的父链上这个限制,我们可以分类讨论,如果b也在a的父链上,那么应该很好求,就是这条链上深度和a相差不超过k的点数目,乘上(a的子树size-1)(减一是因为a自己不算),这就是一个简单的乘法原理,预处理了O(1)就能求出。

      然后是假如b本身就存在于a的一个子树中,距离不超过k,我们知道,一棵子树,在dfs序上是连续的,所以我们在预处理是,以dfs序为版本,以深度为区间,以子树大小为权值建主席树(原因是因为此时我只需要控制深度差范围内的所有贡献,并不一定要知道是具体哪个点的贡献,所以深度一样我们通通加到一个主席树节点权值里)。

      查询?很好说了吧,只要我们知道深度差的范围和这个点对应的子树dfs序区段。

    代码:

     1 #include<bits/stdc++.h>
     2 #define ll long long
     3 using namespace std;
     4 const int N=300005,inf=1e9;
     5 struct node{int y,nxt;}e[N*2];
     6 int n,m,md,h[N],c=0,d[N],sz[N];
     7 int rt[N],cnt,tot,dfn[N],fa[N];
     8 struct ch{int l,r;ll da;}t[N*30];
     9 void add(int x,int y){
    10     e[++c]=(node){y,h[x]};h[x]=c;
    11     e[++c]=(node){x,h[y]};h[y]=c;
    12 } void dfs1(int x){sz[x]=1;
    13     for(int i=h[x],y;i;i=e[i].nxt)
    14     if(!d[y=e[i].y]){
    15         d[y]=d[x]+1;md=max(md,d[y]);
    16         fa[y]=x;dfs1(y);sz[x]+=sz[y];
    17     } return ;
    18 } void update(int &x,int l,int r,ll v,int p){
    19     t[++cnt]=t[x];x=cnt;t[x].da+=v;
    20     if(l==r) return;int mid=l+r>>1;
    21     if(p<=mid) update(t[x].l,l,mid,v,p);
    22     else update(t[x].r,mid+1,r,v,p);
    23 } ll query(int x,int y,int l,int r,int L,int R){
    24     if(L<=l&&r<=R) return t[y].da-t[x].da;
    25     int mid=l+r>>1;ll re=0;
    26     if(L<=mid)re+=query(t[x].l,t[y].l,l,mid,L,R);
    27     if(mid<R)re+=query(t[x].r,t[y].r,mid+1,r,L,R);
    28     return re;
    29 } void dfs2(int x){
    30     dfn[x]=++tot;rt[tot]=rt[tot-1];
    31     update(rt[tot],1,md,sz[x]-1,d[x]);
    32     for(int i=h[x],y;i;i=e[i].nxt){
    33         if((y=e[i].y)==fa[x]) continue;
    34         dfs2(y);
    35     } return ;
    36 } int main(){
    37     scanf("%d%d",&n,&m);
    38     for(int i=1,x,y;i<n;i++)
    39     scanf("%d%d",&x,&y),add(x,y);
    40     d[1]=1;dfs1(1);dfs2(1);
    41     for(int i=1;i<=m;i++){
    42         int p,k;scanf("%d%d",&p,&k);
    43         ll ans=(sz[p]-1)*1ll*(min(k,d[p]-1));
    44         int l=dfn[p]-1,r=dfn[p]+sz[p]-1;
    45         if(d[p]==md){puts("0");continue;}
    46         int z=min(md,d[p]+k);
    47         ans+=query(rt[l],rt[r],1,md,d[p]+1,z);
    48         printf("%lld
    ",ans);
    49     } return 0;
    50 }
    主席树
  • 相关阅读:
    win7 下 qwt安装教程
    qt里标识操作系统的宏
    qt 获取系统磁盘空间大小
    qwt总结1
    debian创建apt-proxy代理
    在Linux使用mingw32来编写win32程序
    Linux环境变量的修改(永久,暂时)
    debian7 oracle11g 解决 link binaries 错误方案
    l​i​n​u​x添加​修​改​用​户​名​密​码
    HOWTO install Oracle 11g on Ubuntu Linux 12.04 (Precise Pangolin) 64bits
  • 原文地址:https://www.cnblogs.com/Alan-Luo/p/10192070.html
Copyright © 2011-2022 走看看