传送门

题意:

  给出一棵树,每条边都有权值;

  给出 m 次询问,每次询问有三个参数 u,v,w ,求节点 u 与节点 v 之间权值 ≤ w 的路径个数;

题解:

  昨天再打比赛的时候,中途,凯少和我说,这道题,一眼看去,就是树链剖分,然鹅,太久没写树链剖分的我一时也木有思路;

  今天上午把树链剖分温习了一遍,做了个模板题;

  下午再想了一下这道题,思路喷涌而出............

  首先,介绍一下相关变量:

  1. int fa[maxn];//fa[u]:u的父节点
  2. int son[maxn];//son[u]:u的重儿子
  3. int dep[maxn];//dep[u]:u的深度
  4. int siz[maxn];//siz[u]:以u为根的子树节点个数
  5. int tid[maxn];//tid[u]:u在线段树中的位置
  6. int top[maxn];//top[u]:u所在重链的祖先节点
  7. int e[maxn][];//e[i][0]与e[i][1]有条权值为e[i][2]的边
  8. vector<int >v[maxn<<];//v[i]:存储线段树中i号节点的所有边的权值

  (树链剖分,默认来看这篇博客的都会辽,逃)  

  下面重点介绍一下v[]的作用(将样例2中的权值改为了10):

  

  由树链剖分可知(图a,紫色部分代表重链)

    tid[1]=1,tid[3]=2,tid[5]=3;

    tid[2]=4,tid[4]=5;

  那么,线段树维护啥呢?

  1. struct SegmentTree
  2. {
  3. int l,r;
  4. int mid()
  5. {
  6. return l+((r-l)>>);
  7. }
  8. }segTree[maxn<<];
  9. vector<int >v[maxn<<];//v[i]:存储线段树中i号节点的所有边的权值

  对于我而言,此次线段树,主要维护节点 i 的左右区间[l,r],重点是 v[] 中维护的东西;

  首先将边权存到线段树中,如何存呢?

  对于边 u,v,w ,(假设 fa[v]=u),将 w 存在 v[ tid[ v ] ]中;

  看一下Update()函数:

  1. //将节点x在线段树中对应的pos位置的v中加入val
  2. void Update(int x,int val,int pos)
  3. {
  4. if(segTree[pos].l == segTree[pos].r)
  5. {
  6. v[pos].push_back(val);//val加入到v[pos]中
  7. return ;
  8. }
  9. int mid=segTree[pos].mid();
  10. if(x <= mid)
  11. Update(x,val,ls(pos));
  12. else
  13. Update(x,val,rs(pos));
  14. }

  例如上图b:

  ①-② : 10 ,调用函数Update(tid[2],10,1) ⇔ v[tid[2]].push_back(10)

  ①-③ : 10 ,调用函数Update(tid[3],10,1) ⇔ v[tid[3]].push_back(10)

  ②-④ : 10 ,调用函数Update(tid[4],10,1) ⇔ v[tid[4]].push_back(10)

  ③-⑤ : 10 ,调用函数Update(tid[5],10,1) ⇔ v[tid[5]].push_back(10)

  线段树中的节点9中的v存储一个10

  线段树中的节点5中的v存储一个10

  线段树中的节点6中的v存储一个10

  线段树中的节点7中的v存储一个10

  这个就是Update()函数的作用;

  接下来的pushUp()函数很重要:

  1. void pushUp(int pos)
  2. {
  3. if(segTree[pos].l == segTree[pos].r)
  4. return;
  5.  
  6. pushUp(ls(pos));
  7. pushUp(rs(pos));
  8.  
  9. //将ls(pos),rs(pos)中的元素存储到pos中
  10. for(int i=;i < v[ls(pos)].size();++i)
  11. v[pos].push_back(v[ls(pos)][i]);
  12. for(int i=;i < v[rs(pos)].size();++i)
  13. v[pos].push_back(v[rs(pos)][i]);
  14. sort(v[pos].begin(),v[pos].end());//升序排列
  15. }

  调用pushUp(1),将所有的pos 的 ls(pos),rs(pos) 节点信息更新到pos节点;

  调用完这个函数后,你会发现:

  v[1]:10,10,10,10([1,5]中的所有节点到其父节点的权值,根节点为null)

  v[2]:10,10([1,3]中的所有节点到其父节点的权值)

  v[3]:10,10([4,5]中的所有节点到其父节点的权值)

  v[4]:10([1,2]中的所有节点到其父节点的权值)

  v[5]:10([3,3]中的所有节点到其父节点的权值)

  v[6]:10([4,4]中的所有节点到其父节点的权值)

  v[7]:10([5,5]中的所有节点到其父节点的权值)

  v[8]:null(根节点为null)

  v[9]:10([2,2]中的所有节点到其父节点的权值)

  你会发现,v[i]中存的值就是[ tree[i].l , tree[i].r ]中所有节点与其父节点的权值;

  接下来就是询问操作了:

  1. int BS(int pos,int w)
  2. {
  3. int l=-,r=v[pos].size();
  4. while(r-l > )
  5. {
  6. int mid=l+((r-l)>>);
  7. if(v[pos][mid] <= w)
  8. l=mid;
  9. else
  10. r=mid;
  11. }
  12. return l+;
  13. }
  14. int Query(int l,int r,int pos,int w)
  15. {
  16. if(v[pos][] > w)//当前区间的如果最小的值要 > w,直接返回0
  17. return ;
  18. if(segTree[pos].l == l && segTree[pos].r == r)
  19. return BS(pos,w);//二分查找pos区间值 <= w 得个数(还记得pushUp()中的sort函数么?
  20.  
  21. int mid=segTree[pos].mid();
  22. if(r <= mid)
  23. return Query(l,r,ls(pos),w);
  24. else if(l > mid)
  25. return Query(l,r,rs(pos),w);
  26. else
  27. return Query(l,mid,ls(pos),w)+Query(mid+,r,rs(pos),w);
  28. }

AC代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ls(x) (x<<1)
  4. #define rs(x) (x<<1|1)
  5. #define INF 0x3f3f3f3f
  6. #define mem(a,b) memset(a,b,sizeof(a))
  7. const int maxn=1e5+;
  8.  
  9. int n,m;
  10. int fa[maxn];//fa[u]:u的父节点
  11. int son[maxn];//son[u]:u的重儿子
  12. int dep[maxn];//dep[u]:u的深度
  13. int siz[maxn];//siz[u]:以u为根的子树节点个数
  14. int tid[maxn];//tid[u]:u在线段树中的位置
  15. int top[maxn];//top[u]:u所在重链的祖先节点
  16. int e[maxn][];//e[i][0]与e[i][1]有条权值为e[i][2]的边
  17. vector<int >v[maxn<<];//v[i]:存储线段树中i号节点的所有边的权值
  18. int num;
  19. int head[maxn];
  20. struct Edge
  21. {
  22. int to;
  23. int w;
  24. int next;
  25. }G[maxn<<];
  26. void addEdge(int u,int v,int w)
  27. {
  28. G[num].to=v;
  29. G[num].w=w;
  30. G[num].next=head[u];
  31. head[u]=num++;
  32. }
  33. struct SegmentTree
  34. {
  35. int l,r;
  36. int mid()
  37. {
  38. return l+((r-l)>>);
  39. }
  40. }segTree[maxn<<];
  41. void DFS1(int u,int f,int depth)
  42. {
  43. fa[u]=f;
  44. son[u]=-;
  45. siz[u]=;
  46. dep[u]=depth;
  47. for(int i=head[u];~i;i=G[i].next)
  48. {
  49. int v=G[i].to;
  50. if(v == f)
  51. continue;
  52. DFS1(v,u,depth+);
  53.  
  54. siz[u] += siz[v];
  55.  
  56. if(son[u] == - || siz[v] > siz[son[u]])
  57. son[u]=v;
  58. }
  59. }
  60. void DFS2(int u,int anc,int &k)
  61. {
  62. top[u]=anc;
  63. tid[u]=++k;
  64. if(son[u] == -)
  65. return ;
  66. DFS2(son[u],anc,k);
  67.  
  68. for(int i=head[u];~i;i=G[i].next)
  69. {
  70. int v=G[i].to;
  71. if(v != fa[u] && v != son[u])
  72. DFS2(v,v,k);
  73. }
  74. }
  75. void pushUp(int pos)
  76. {
  77. if(segTree[pos].l == segTree[pos].r)
  78. return;
  79.  
  80. pushUp(ls(pos));
  81. pushUp(rs(pos));
  82.  
  83. //将ls(pos),rs(pos)中的元素存储到pos中
  84. for(int i=;i < v[ls(pos)].size();++i)
  85. v[pos].push_back(v[ls(pos)][i]);
  86. for(int i=;i < v[rs(pos)].size();++i)
  87. v[pos].push_back(v[rs(pos)][i]);
  88. sort(v[pos].begin(),v[pos].end());//升序排列
  89. }
  90. void buildSegTree(int l,int r,int pos)
  91. {
  92. segTree[pos].l=l;
  93. segTree[pos].r=r;
  94. if(l == r)
  95. return ;
  96.  
  97. int mid=l+((r-l)>>);
  98. buildSegTree(l,mid,ls(pos));
  99. buildSegTree(mid+,r,rs(pos));
  100. }
  101. //将节点x在线段树中对应的pos位置的v中加入val
  102. void Update(int x,int val,int pos)
  103. {
  104. if(segTree[pos].l == segTree[pos].r)
  105. {
  106. v[pos].push_back(val);//val加入到v[pos]中
  107. return ;
  108. }
  109. int mid=segTree[pos].mid();
  110. if(x <= mid)
  111. Update(x,val,ls(pos));
  112. else
  113. Update(x,val,rs(pos));
  114. }
  115. int BS(int pos,int w)
  116. {
  117. int l=-,r=v[pos].size();
  118. while(r-l > )
  119. {
  120. int mid=l+((r-l)>>);
  121. if(v[pos][mid] <= w)
  122. l=mid;
  123. else
  124. r=mid;
  125. }
  126. return l+;
  127. }
  128. int Query(int l,int r,int pos,int w)
  129. {
  130. if(v[pos][] > w)//当前区间的如果最小的值要 > w,直接返回0
  131. return ;
  132. if(segTree[pos].l == l && segTree[pos].r == r)
  133. return BS(pos,w);//二分查找pos区间值 <= w 得个数(还记得pushUp()中的sort函数么?
  134.  
  135. int mid=segTree[pos].mid();
  136. if(r <= mid)
  137. return Query(l,r,ls(pos),w);
  138. else if(l > mid)
  139. return Query(l,r,rs(pos),w);
  140. else
  141. return Query(l,mid,ls(pos),w)+Query(mid+,r,rs(pos),w);
  142. }
  143. int Find(int u,int v,int w)//查询节点u到节点v之间权值小于等于w得路径个数
  144. {
  145. int ans=;
  146. int topU=top[u];
  147. int topV=top[v];
  148. while(topU != topV)
  149. {
  150. if(dep[topU] > dep[topV])
  151. {
  152. swap(u,v);
  153. swap(topU,topV);
  154. }
  155. ans += Query(tid[top[v]],tid[v],,w);
  156. v=fa[topV];
  157. topV=top[v];
  158. }
  159. if(u == v)
  160. return ans;
  161. if(dep[u] > dep[v])
  162. swap(u,v);
  163. return ans+Query(tid[son[u]],tid[v],,w);
  164. }
  165. void Solve()
  166. {
  167. DFS1(,,);
  168. int k=;
  169. DFS2(,,k);
  170.  
  171. buildSegTree(,k,);
  172.  
  173. for(int i=;i < n;++i)
  174. {
  175. if(dep[e[i][]] > dep[e[i][]])
  176. swap(e[i][],e[i][]);//令fa[e[i][1]] = e[i][0],方便更新操作
  177. Update(tid[e[i][]],e[i][],);//将e[i][2]加入到tid[e[i][1]]中
  178. }
  179. pushUp();//更新线段树中所有的pos
  180.  
  181. for(int i=;i <= m;++i)
  182. {
  183. int u,v,w;
  184. scanf("%d%d%d",&u,&v,&w);
  185. printf("%d\n",Find(u,v,w));
  186. }
  187. }
  188. void Init()
  189. {
  190. num=;
  191. mem(head,-);
  192. for(int i=;i < *maxn;++i)
  193. v[i].clear();
  194. }
  195. int main()
  196. {
  197. // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
  198. while(~scanf("%d%d",&n,&m))
  199. {
  200. Init();
  201. for(int i=;i < n;++i)
  202. {
  203. scanf("%d%d%d",e[i]+,e[i]+,e[i]+);
  204. addEdge(e[i][],e[i][],e[i][]);
  205. addEdge(e[i][],e[i][],e[i][]);
  206. }
  207. Solve();
  208. }
  209. return ;
  210. }

2019南昌邀请赛网络预选赛 J.Distance on the tree(树链剖分)的更多相关文章

  1. 2019南昌邀请赛网络赛:J distance on the tree

    1000ms 262144K   DSM(Data Structure Master) once learned about tree when he was preparing for NOIP(N ...

  2. 2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树

    边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<st ...

  3. 计蒜客 2019南昌邀请网络赛J Distance on the tree(主席树)题解

    题意:给出一棵树,给出每条边的权值,现在给出m个询问,要你每次输出u~v的最短路径中,边权 <= k 的边有几条 思路:当时网络赛的时候没学过主席树,现在补上.先树上建主席树,然后把边权交给子节 ...

  4. 2019南昌邀请赛网络预选赛 M. Subsequence

    传送门 题意: 给出一个只包含小写字母的串 s 和n 个串t,判断t[i]是否为串 s 的子序列: 如果是,输出"YES",反之,输出"NO": 坑点: 二分一 ...

  5. 2019南昌邀请赛网络预选赛 I. Max answer(单调栈+暴力??)

    传送门 题意: 给你你一序列 a,共 n 个元素,求最大的F(l,r): F(l,r) = (a[l]+a[l+1]+.....+a[r])*min(l,r); ([l,r]的区间和*区间最小值,F( ...

  6. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  7. 南昌网络赛J. Distance on the tree 树链剖分

    Distance on the tree 题目链接 https://nanti.jisuanke.com/t/38229 Describe DSM(Data Structure Master) onc ...

  8. 南昌网络赛J. Distance on the tree 树链剖分+主席树

    Distance on the tree 题目链接 https://nanti.jisuanke.com/t/38229 Describe DSM(Data Structure Master) onc ...

  9. 2019南昌网络赛 J Distance on the tree 主席树+lca

    题意 给一颗树,每条边有边权,每次询问\(u\)到\(v\)的路径中有多少边的边权小于等于\(k​\) 分析 在树的每个点上建\(1​\)到\(i​\)的权值线段树,查询的时候同时跑\(u,v,lca ...

随机推荐

  1. ES6使用的一些方法

    查找数组中符合条件的所有记录 var list=[ {id:1,name:"张三"}, {id:2,name:"李四"}, {id:3,name:"王 ...

  2. 我认知的javascript之函数调用

    今天刚好周六没事,又由于工作的原因导致早上醒来就睡不着,无聊之下,就想到了 js 的function调用问题.当然,网上也是对javascript的一些事情说得很透了,但我觉得还是有必要把自己的想法说 ...

  3. "'cl' 不是内部或外部命令,也不是可运行的程序"解决方案

    最近使用VS2012+Qt5.1+QtCreator2.8.1来搭建Qt的开发环境(之前有用MinGW编译的经历,经常碰到gdb调试器崩溃的问题),全部换成想用VC的编译器和调试环境,但是觉得QtCr ...

  4. PHP生成PDF并转换成图片爬过的坑

    需求描述:根据订单通过模板合同生成新的PDF合同通过e签宝签约后转为图片给用户下载. 需求整理: 1.如何生成PDF文件:使用TCPDF扩展生成.思考: ⑴为了方便将模板中的固定占位符替换为订单中的内 ...

  5. formatter的使用

    1.目的 如图所示,实现行编辑栏中的编辑删除,以及在时间建议中显示上中下旬 可参考easyui官方文档中所写的关于datagrid列属性:http://www.jeasyui.net/plugins/ ...

  6. me

    PXKUNUIN6A- eyJsaWNlbnNlSWQiOiJQWEtVTlVJTjZBIiwibGljZW5zZWVOYW1lIjoi5b285bK4IDEiLCJhc3NpZ25l ZU5hbWU ...

  7. [已决解]关于Hadoop start-all.sh启动问题

    问题一:出现Attempting to operate on hdfs namenode as root 写在最前注意: 1.master,slave都需要修改start-dfs.sh,stop-df ...

  8. 二维数组中的查找[by Python]

    题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...

  9. asp.net webapi中helppage

    今天研究了下webapi,发现还有自动生成接口说明文档提供测试的功能 参考:https://docs.microsoft.com/en-us/aspnet/web-api/overview/getti ...

  10. Top Page

    Top Page 由于个人的博客中涉及了几个不同的领域.今后准备设置Index页进行一番整理 : 所有其他页面都可以从这个页面遍历 Top Page