[湖南集训] 谈笑风生
题意:
设 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\n",ans); 49 } return 0; 50 }
主席树
转载于:https://www.cnblogs.com/Alan-Luo/p/10192070.html
[湖南集训] 谈笑风生相关推荐
- 洛谷 - P3899 [湖南集训]谈笑风生(dfs序+主席树/二维数点)
题目链接:点击查看 题目大意:设 TTT 为一棵有根树,我们做如下的定义: 设 aaa 和 bbb 为 TTT 中的两个不同节点.如果 aaa 是 bbb 的祖先,那么称"aaa 比 bbb ...
- P3899 [湖南集训]谈笑风生(线段树合并)
P3899 [湖南集训]谈笑风生 给定一颗以111号节点为根的树,如果a≠ba \neq ba=b,且aaa是bbb的祖先,则aaa比bbb更厉害,如果a≠ba \neq ba=b,且dis( ...
- P3899 [湖南集训]谈笑风生
P3899 [湖南集训]谈笑风生 题目描述 Solution 我们考虑离线询问,将询问放在相对应的子树ppp中计算答案. 显然a,b,ca,b,ca,b,c的位置关系有两种情况: bbb是aaa的祖先 ...
- 洛谷P3899 [湖南集训]谈笑风生
Description 设T 为一棵有根树,我们做如下的定义: • 设a和b为T 中的两个不同节点.如果a是b的祖先,那么称"a比b不知道 高明到哪里去了". • 设a 和 b 为 ...
- luogu P3899 [湖南集训]谈笑风生 线段树合并
Description 设T 为一棵有根树,我们做如下的定义: ? 设a和b为T 中的两个不同节点.如果a是b的祖先,那么称"a比b不知道 高明到哪里去了". ? 设a 和 b 为 ...
- [湖南集训]谈笑风生
谈笑风生 题目大意: 给定a,求任何一个距 a 距离不超过给定的 k 的 b ,然后求一个 c 使得其为 a,b 的后代,求这样的三元组共有多少个. 数据结构好题啊! 解法分析: 我们可以深搜两遍(其 ...
- bzoj 3653 [湖南集训]谈笑风生
题目描述 设 T 为一棵有根树,我们做如下的定义: • 设 a 和 b 为 T 中的两个不同节点.如果 a 是 b 的祖先,那么称"a 比 b 不知道高明到哪里去了". • 设 a ...
- P3899 [湖南集训]谈笑风生 主席树解决二维数点
传送门 文章目录 题意: 思路: 题意: 思路: 由于a,ba,ba,b都比ccc厉害,那么a,ba,ba,b一定是某个是某个的祖先.那么就分为两种情况了: (1)(1)(1) bbb在aaa上面,约 ...
- [主席树] 湖南集训 谈笑风生
传送门 一.做法 CCC一定是A,BA, BA,B公共子树里的点 B在A上方 则C一定在A子树中 ans=min{k,dep[A]−1}×(siz[A]−1)ans=\min\{k,dep[A]-1 ...
最新文章
- python五十五:__getattribute__内置函数
- 无线通信 -- 跳频技术
- ERROR: No matching distribution found for onnxsim
- 成功解决TypeError: ‘encoding’ is an invalid keyword argument for this function
- 文献记录(part1)--NP-hardness of Euclidean sum-of-squares clustering
- python 回溯法 子集树模板 系列 —— 1、8 皇后问题
- 数据预处理和特征工程
- saltstack mysql_saltstack学习五:return及入库_MySQL
- american fuzzy lop 介绍
- 【MySQL】mysql死锁以及死锁日志分析
- vue 上下滚动的菜单_vue实现广告栏上下滚动效果
- 信鸽 ios tag推送 php,信鸽推送集成
- 2019-成长的一年
- python协程处理海量文件_Python使用asyncio和run-In-Executor线程池处理多个文件的同时下载,python,协程,加,runinexecutor...
- 三线性插值(Trilinear Interpolation)详解
- 使用C#启动默认Internet浏览器
- c语言实现在数组中找一个数字显示,C语言找出数组中的特定元素的算法解析
- 使用GNURadio和RTL-SDR搭建FM广播接收机
- 【Linux】安装 Ubuntu18.04 全程实录(安装 NVIDIA 驱动,安装主题美化,安装应用程序,网易云无法启动,GoldenDict 音标显示不正常)
- Poker 键盘试用
热门文章
- Tricks Device 最短路+最大流
- python列表内元素求和_Python之list对应元素求和
- Java常用算法二:分治法
- C++11新特性之 可变参数模板
- TCP和UDP的不同
- bzoj 1664: [Usaco2006 Open]County Fair Events 参加节日庆祝(DP)
- [paper reading] YOLO v1
- 最新高清IT职业技能图谱(15图)
- matlab2c使用c++实现matlab函数系列教程-histc函数
- python递归的方式打印九九乘法表