嗯这题是一道对树进行动态修改&查询的经典题目,可以拿来练习树链剖分~

  啊对于这种动态修改&查询的题目,我们最喜闻乐见的就是在一个序列上去做了,毕竟可以直接套各种数据结构模版啊,比如线段树、平衡树之类的。那么对于这种树上的动态修改&查询,我们可以把它通过一定的手段,“转化”成序列上的问题,再套用xx树之类的数据结构进行快速维护。而这个手段呢,就有很多种了(应该是吧?),这里用到的树链剖分,就是一种将树转化成序列的划分方式。

  好的,我们现在拿到一棵树,首先我们会看到,这个树跟序列几乎没半点长的像的地方T_T,除非当这个坑爹的树刚好是一条链的形状……诶等等?链?对,就是那个极端情况下平衡树会退化成的那种样子 —— 一条链= = 联想到了什么?没错,我们可以把树拆成一条条链,嗯,我们可以这样想像一下:首先我们手里有一条链,然后我们再拿过来一条链,把它接在链中间的某个位置上,它就有了个分支,然后我们再接几条链上来,诶没错,它就成了一棵树!(怎么感觉有点像鸡毛掸子似的)

  也就是说,我们可以将一棵树拆成几条链,平放在一条线上,它就成了一个序列了~

  现在问题来了:怎么拆?首先我们在树上进行的查询,经常是对于两点间的【路径】的查询,那么我们肯定希望我们拆出来的链,尽可能是连续的大段,而不是细碎的小段,因为我们在用数据结构维护的时候,肯定是维护树上连续的链比较方便。而这种“长链”,就是在树链剖分中我们称之为【重链】的东西。但是,如果只有一条条重链也组不成一棵树啊,所以我们需要【轻链】来将重链连接起来,这样,我们对于树上所有的点和边,就都划分开了。

  这个工作我们可以通过两次dfs来完成:一次dfs求出所有节点的father,son(这个son专指重儿子,重儿子的重儿子连下去组成重链),size,deep求出来……

  (此处省略500字)好吧其实我还是有节操一点,把实现过程传送一下吧:http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html

  思想就是:路径可以划分为一条或多条重链的加和。(仅考虑【点】)

  不在同一重链上,就往同一条重链上靠,这个是让深度大的往深度小的上面靠(想一想,为什么?),同时对深度大的重链上的值进行 维护or查询;如果在同一重链上,就回归了我们熟知的序列上的 维护or查询 问题了。

  以下是BZOJ1036的代码:

  1. /**************************************************************
  2. Problem: 1036
  3. User: Tunix
  4. Language: C++
  5. Result: Accepted
  6. Time:2356 ms
  7. Memory:5612 kb
  8. ****************************************************************/
  9.  
  10. //BZOJ 1036
  11. #include<vector>
  12. #include<cstdio>
  13. #include<cstring>
  14. #include<cstdlib>
  15. #include<iostream>
  16. #include<algorithm>
  17. #define rep(i,n) for(int i=0;i<n;++i)
  18. #define F(i,j,n) for(int i=j;i<=n;++i)
  19. #define D(i,j,n) for(int i=j;i>=n;--i)
  20. #define pb push_back
  21. using namespace std;
  22. const int N=,INF=~0u>>;
  23. typedef long long LL;
  24. //#define debug
  25. struct Tree{
  26. int max,sum;
  27. #define L o<<1
  28. #define R o<<1|1
  29. }t[N<<];
  30. vector<int>G[N];
  31. int n,m;
  32. int tid[N],top[N],fa[N],son[N],dep[N],cnt,tot,size[N],a[N];
  33. bool vis[N];
  34.  
  35. //从这里到undef为线段树上的操作
  36. #define mid (l+r>>1)
  37. inline void maintain(int o,int l,int r){
  38. t[o].max=t[o].sum=;
  39. if(l<r){
  40. t[o].max=max(t[L].max,t[R].max);
  41. t[o].sum=t[L].sum+t[R].sum;
  42. }
  43. }
  44.  
  45. void updata(int o,int l,int r,int pos,int v){
  46. if (l==r) t[o].max=t[o].sum=v;
  47. else{
  48. if (pos<=mid) updata(L,l,mid,pos,v);
  49. if (pos>mid) updata(R,mid+,r,pos,v);
  50. maintain(o,l,r);
  51. }
  52. }
  53. int ql=,qr=;
  54. int _max,_sum;
  55. void query_it(int o,int l,int r){
  56. if (ql<=l && qr>=r){
  57. _max=max(_max,t[o].max);
  58. _sum+=t[o].sum;
  59. }
  60. else{
  61. if (ql<=mid) query_it(L,l,mid);
  62. if (qr>mid) query_it(R,mid+,r);
  63. }
  64. }
  65.  
  66. #undef mid
  67. //线段树end
  68.  
  69. void dfs(int x,int father,int deep){//第一次dfs
  70. vis[x]=;
  71. fa[x]=father; dep[x]=deep; size[x]=; son[x]=;
  72. int maxsize=;
  73. rep(i,G[x].size()){
  74. int to=G[x][i];
  75. if (vis[to]) continue;
  76. dfs(to,x,deep+);
  77. size[x]+=size[to];
  78. if (size[to]>maxsize) maxsize=size[to],son[x]=to;
  79. }
  80. }
  81.  
  82. void connect(int x,int f){//第二次dfs,进行重链连接
  83. vis[x]=;
  84. tid[x]=++tot; top[x]=f;
  85. if (son[x]) connect(son[x],f);
  86.  
  87. rep(i,G[x].size()){
  88. int to=G[x][i];
  89. if (!vis[to]) connect(to,to);
  90. }
  91. }
  92.  
  93. void query(int x,int y){//树上查询
  94. while(top[x]!=top[y]){//如果不在同一重链上
  95. if (dep[top[x]]<dep[top[y]])
  96. swap(x,y);//找到深度大的
  97. ql=tid[top[x]]; qr=tid[x];
  98. query_it(,,n);//查询这条重链
  99. x=fa[top[x]];//往深度浅的靠
  100. }
  101. //直到在同一重链上,循环结束
  102. if (dep[x]>dep[y]) swap(x,y);
  103. ql=tid[x]; qr=tid[y];
  104. query_it(,,n);//查询这段区间
  105. }
  106.  
  107. int main(){
  108. #ifndef ONLINE_JUDGE
  109. freopen("file.in","r",stdin);
  110. // freopen("file.out","w",stdout);
  111. #endif
  112. int x,y;
  113. scanf("%d",&n);
  114. F(i,,n){
  115. scanf("%d%d",&x,&y);
  116. G[x].pb(y);
  117. G[y].pb(x);
  118. }
  119. F(i,,n) scanf("%d",&a[i]);
  120. memset(vis,,sizeof vis);
  121. dfs(,,);
  122. memset(vis,,sizeof vis);
  123. connect(,);
  124. F(i,,n) updata(,,n,tid[i],a[i]);
  125.  
  126. scanf("%d",&m);
  127. char cmd[];
  128. F(i,,m){
  129. scanf("%s%d%d",cmd,&x,&y);
  130. if (cmd[]=='H')
  131. updata(,,n,tid[x],y);
  132. else{
  133. _sum=;
  134. _max=-INF;
  135. query(x,y);
  136. if(cmd[]=='M') printf("%d\n",_max);
  137. else printf("%d\n",_sum);
  138. }
  139. }
  140. return ;
  141. }
  142. /******************************************************
  143. 树链剖分啊,感觉上也是一种把树转化为序列进行操作的过程
  144. 一棵树不好存,就拆成一条条链,平放在一起就成了一个序列
  145. 然后根据一定的方式来拆,可以保证链的数量尽量少(log(n))
  146. 然后就可以用序列操作的方式,对树进行操作了!
  147. ******************************************************/

【BZOJ】【1036】树的统计的更多相关文章

  1. BZOJ 1036 树的统计-树链剖分

    [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 12904 Solved: 5191[Submit][Status ...

  2. Codevs 2460 == BZOJ 1036 树的统计

     2460 树的统计 2008年省队选拔赛浙江 时间限制: 2 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 一棵树上有n个节点,编号分别为1 ...

  3. BZOJ 1036 树的统计

    Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. Q ...

  4. BZOJ 1036 树的统计Count 树链剖分模板题

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1036 题目大意: 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将 ...

  5. [置顶] bzoj 1036 树的统计Count 点权值模板

    树链剖分 点权型可做模板,链路剖分的思想把点hash到线段树的上,然后可通过n*(log(n)*log(n))的复杂度在树上操作,在线段树上能操作的在链路上都能操作. #include<cstd ...

  6. BZOJ 1036 树的统计(树链剖分)

    点权树链剖分模板题. # include <cstdio> # include <cstring> # include <cstdlib> # include &l ...

  7. bzoj 1036 树的统计Count

    题目大意: 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u ...

  8. BZOJ - 1036 树的统计Count (LCT)

    LCT试炼题(代码量居然完爆树剖?) #include<bits/stdc++.h> using namespace std; ,inf=0x3f3f3f3f; ],flp[N],n,m, ...

  9. Bzoj 1036 树的统计 分类: ACM TYPE 2014-12-29 18:55 72人阅读 评论(0) 收藏

    Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. Q ...

  10. BZOJ 1036 树的统计 | 树链剖分模板题

    又做了一遍--去掉读入优化只有八十行~ #include <cstdio> #include <cstring> #include <algorithm> usin ...

随机推荐

  1. 利用脚本设置本机IP地址

    各位同学,在日常工作中.常出现需要指定IP的地址的清况.为了解决这一个问题,我特意为自己编写了一段脚本.方便设定自己笔记本的IP地址.供大家参考. 其中包括无线wifi和有线网络设定两个IP的操作. ...

  2. Python 字典(Dictionary)操作详解

    Python 字典(Dictionary)的详细操作方法. Python字典是另一种可变容器模型,且可存储任意类型对象,如字符串.数字.元组等其他容器模型. 一.创建字典 字典由键和对应值成对组成.字 ...

  3. mysql 语句其它及优化

    将检索到的数据保存到文件  Select * into outfile ‘文件地址’ from tabname; 生成的文件以制表符区分字段,以换行符区分记录 为满足特殊需求会采用不同的分割方式. 支 ...

  4. js闭包理解实例小结

    Js闭包 闭包前要了解的知识  1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...

  5. Dalvik opcodes

    原文地址: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html Dalvik opcodes Author: Gabor Paller V ...

  6. php网页,想弹出对话框, 消息框 简单代码

    php网页,想弹出对话框, 消息框 简单代码 <?php echo "<script language=\"JavaScript\">alert(\&q ...

  7. 正整数转换成N进制的数组

    给定一个正整数,按照N进制转换成数组元素存储 //给定一个整数,把它转换成按照N进制存储的数组 #include <stdio.h> #include <stdlib.h> # ...

  8. Redis 配置文件 redis.conf 项目详解

    Redis.conf 配置文件详解 # [Redis](http://yijiebuyi.com/category/redis.html) 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, ...

  9. super的用法

    1.调用父类的构造方法子类可以调用由父类声明的构造方法.但是必须在子类的构造方法中使用super关键字来调用. 2.操作被隐藏的成员变量和被覆盖的成员方法如果想在子类中操作父类中被隐藏的成员变量和被覆 ...

  10. hdu 1429 胜利大逃亡(续)

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1429 胜利大逃亡(续) Description Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王 ...