[湖南集训] 谈笑风生

Posted alan-luo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[湖南集训] 谈笑风生相关的知识,希望对你有一定的参考价值。

题意:

  设 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 }
主席树

 



以上是关于[湖南集训] 谈笑风生的主要内容,如果未能解决你的问题,请参考以下文章

[湖南集训] 谈笑风生 (主席树)

P3899 [湖南集训]谈笑风生

[湖南集训]谈笑风生

P3899 [湖南集训]谈笑风生

[湖南集训] 谈笑风生

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