codevs 1228 苹果树 树链剖分讲解

题目:codevs 1228 苹果树

链接:http://codevs.cn/problem/1228/

看了这么多树链剖分的解释,几个小时后总算把树链剖分弄懂了。

树链剖分的功能:快速修改,查询树上的路径。

比如一颗树

首先,我们要把树剖分成树链。定义:

fa[x]是x节点的上一层节点(就是他的爸爸)。

deep[x]是x节点的深度。

num[x]是x节点下面的子节点的数量(包括自己)

son[x]重儿子:一个节点的儿子的num[x]值最大的节点。其他的儿子都是轻儿子。

重链:重儿子连接在一起的路径,比如上图粗线就是重链(叶节点也是重链,只不过它只有一个点)。

     重链之间是用一条轻链边连接的。

top[x]是每条重链的根节点,即是上图中的红色点。

tree[x]是数上节点在线段树上的编号

ftree[x]是线段树上节点在原来树的节点号

现在把它放到线段树里,从根节点开始编号为1,沿着重链走,每走到一个节点给它编号(可以用一个topa变量记录下一个编号),重链走完了走轻链。如图所示就给每条边都编上号了。如果边的长度没有,当然也可以把节点放在线段树上。图总的蓝色数字就是这条边在线段树里的位置,形成了区间,如下图。

然后把这个数组组成最终的线段树,就可以控制它的区间了。

可以发现,虽然看上去把树剖分放到线段树上好像打乱了树的顺序,线段树中的点仍然有原来树的影子。比如如果我要访问x节点的子树,那么这个节点的子树的区间就是从tree[x]到tree[x]+num[x]-1(-1是减掉自己这个节点)的区间。

我们可以用2个dfs来把剖分的动作实现。

第一个dfs先实现fa[x],deep[x],num[x]的计算,num要在访问完子树之后计算,见代码:

 1 void dfs1(int x)
 2 {
 3     num[x]++;
 4     for(int i=0;i<map[x].size();i++)
 5     {
 6         int dd=map[x][i];
 7         if(dd!=fa[x])
 8         {
 9             fa[dd]=x;
10             deep[dd]=deep[x]+1;
11             dfs1(dd);
12             num[x]+=num[dd];
13         }
14     }
15 }

注释:map是STL的vector,用来储存边。

第二个dfs完成son[x],tree[x],ftree[x]的计算,代码如下:

 1 void dfs2(int x)
 2 {
 3     topa++;
 4     ftree[topa]=x;
 5     A[topa]++;
 6     tree[x]=topa;
 7     int zi=0,mx=0;
 8     for(int i=0;i<map[x].size();i++)
 9     {
10         int dd=map[x][i];
11         if(num[dd]>mx)
12         {
13             mx=num[dd];
14             zi=dd;
15         }
16     }
17     if(zi!=0) dfs2(zi); else return;
18     son[x]=zi;
19     for(int i=0;i<map[x].size();i++)
20     {
21         int dd=map[x][i];
22         if(dd!=zi) dfs2(dd);
23     }
24 }

剖分动作结束,接下来是线段树的事情了。

这里再说一下如何在线段树上操作原树,之前提到过,其实在线段树上也有原来树的结构。

x的子树区间就是tree[x]到tree[x]+num[x]-1。

下面来看一下这道题:codevs 1228 苹果树

这是一个最基本的树链剖分。题目中要求计算一颗子树上有苹果多少颗,改变是点修改。因此只要找到那个节点,子树在线段树上的位置,线段树是维护某区间的苹果树数量,查询操作就是一般的线段树查询。

代码:

  1 #include<cstdio>
  2 #include<vector>
  3 #include<iostream>
  4 using namespace std;
  5 const int maxn=100010;
  6
  7 vector<int> map[maxn];
  8 int fa[maxn],n,deep[maxn],num[maxn],topa,A[maxn],tree[maxn],ftree[maxn],son[maxn],sumv[maxn*4],k;
  9
 10 void dfs1(int x)
 11 {
 12     num[x]++;
 13     for(int i=0;i<map[x].size();i++)
 14     {
 15         int dd=map[x][i];
 16         if(dd!=fa[x])
 17         {
 18             fa[dd]=x;
 19             deep[dd]=deep[x]+1;
 20             dfs1(dd);
 21             num[x]+=num[dd];
 22         }
 23     }
 24 }
 25
 26 void dfs2(int x)
 27 {
 28     topa++;
 29     ftree[topa]=x;
 30     A[topa]++;
 31     tree[x]=topa;
 32     int zi=0,mx=0;
 33     for(int i=0;i<map[x].size();i++)
 34     {
 35         int dd=map[x][i];
 36         if(num[dd]>mx)
 37         {
 38             mx=num[dd];
 39             zi=dd;
 40         }
 41     }
 42     if(zi!=0) dfs2(zi); else return;
 43     son[x]=zi;
 44     for(int i=0;i<map[x].size();i++)
 45     {
 46         int dd=map[x][i];
 47         if(dd!=zi) dfs2(dd);
 48     }
 49 }
 50
 51 void init(int o,int L,int R)
 52 {
 53     if(L==R) sumv[o]=A[L];
 54     else
 55     {
 56         int M=(L+R)/2;
 57         init(o*2,L,M);
 58         init(o*2+1,M+1,R);
 59         sumv[o]=sumv[o*2]+sumv[o*2+1];
 60     }
 61 }
 62
 63 int y1,y2,p;
 64 void update(int o,int L,int R)
 65 {
 66     if(L==R) sumv[o]=(sumv[o]+1)%2;
 67     else
 68     {
 69         int M=(L+R)/2;
 70         if(p<=M) update(o*2,L,M);
 71         else update(o*2+1,M+1,R);
 72         sumv[o]=sumv[o*2]+sumv[o*2+1];
 73     }
 74 }
 75
 76 int ans;
 77 void query(int o,int L,int R)
 78 {
 79     if(y1<=L && R<=y2) ans+=sumv[o];
 80     else
 81     {
 82         int M=(L+R)/2;
 83         if(y1<=M) query(o*2,L,M);
 84         if(y2>M) query(o*2+1,M+1,R);
 85     }
 86 }
 87
 88 int main()
 89 {
 90     cin>>n;
 91     for(int i=1,x,y;i<=n-1;i++)
 92     {
 93         cin>>x>>y;
 94         map[x].push_back(y);
 95     }
 96     deep[1]=1;
 97     dfs1(1);
 98     dfs2(1);
 99
100     init(1,1,n);
101
102     cin>>k;
103     for(int i=1,x;i<=k;i++)
104     {
105         char tp;
106         cin>>tp;
107         if(tp==‘C‘)
108         {
109             cin>>x;
110             p=tree[x];
111             update(1,1,n);
112         }
113         else
114         {
115             cin>>x;
116             y1=tree[x];
117             y2=y1+num[x]-1;
118             ans=0;
119             query(1,1,n);
120             cout<<ans<<endl;
121         }
122     }
123     return 0;
124 }
时间: 2024-10-03 14:42:04

codevs 1228 苹果树 树链剖分讲解的相关文章

树链剖分讲解&amp;模板

原理&实现 线段树是一种可以快速进行区间修改和查询的数据结构,并且我们已经知道可以通过dfs序加线段树来维护一棵子树的信息,那么,有没有一种方法来维护树上的两个点之间的链的信息的方法呢?当然是有的,这时就要请出树链剖分了. 对于一棵有根树,我们维护两个节点之间的路径信息时,可以想到维护两个点到lca的分别的信息,既然是要维护一条链,那么我们尽可能把树的节点进行重新编号,使其有更多连续的编号的节点形成链,这样我们更容易在线段树里去维护它. 所以,我们需要把树上的链分为重链和轻链,重链是由一系列重儿

codevs 4633 [Mz]树链剖分练习

时间限制: 1 s 空间限制: 64000 KB 题目等级 : 大师 Master 题目描述 Description 给定一棵结点数为n的树,初始点权均为0,有依次q个操作,每次操作有三个参数a,b,c,当a=1时,表示给b号结点到c号结点路径上的所有点(包括b,c,下同)权值都增加1,当a=2时,表示询问b号结点到c号结点路径上的所有点权值之和. 输入描述 Input Description 第一行,一个正整数n. 接下来n-1行,每行一对正整数x,y,表示x号结点和y号结点之间有一条边. 第

【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)

3589: 动态树 Time Limit: 30 Sec  Memory Limit: 1024 MBSubmit: 405  Solved: 137[Submit][Status][Discuss] Description 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0: 这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1: 小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝

树链剖分详解及模板

这几天学习了一下树链剖分,顺便写一下我的理解. 早上看了一下别人的讲解,云里雾里,终于算是搞懂了. 树链剖分是解决在树上进行插点问线,插线问点等一系列树上的问题 假如现在给你一棵树,然后没两条边之间有一条权值,有一些操作,1:x---y之间的最大权值是多少,2:改变x---y之间的权值 当前这样的操作有很多,如果直接用暴力的方法的话肯定不行,那么就要想一个好的方法,我们可以想一下能不能借助线段树解决,能不能想一种方法对树上的边进行编号,然后就变成区间了.那么我们就可以在线段树上进行操作了,树链剖

spoj Query on a tree(树链剖分模板题)

375. Query on a tree Problem code: QTREE You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form: CHANGE i ti : change the cost of

模板:树链剖分

“如果你会了树上dp,还会线段树……” “没错!我都会啊!” “……那你为什么写不出树链剖分?” “???” ——by勇者和路由器的对话,今天二位仍然过得十分愉快 ———————————————————————————— 因为路由器编不出来什么好题面了,所以就扔上来了一个模板题然后和勇者去玩了. (下面有树链剖分的板子代码+注释,这里只提供讲解) [ZJOI2008]树的统计Count:http://www.cnblogs.com/luyouqi233/p/7886531.html 我们发现题中

【Luogu】P3313旅行(树链剖分)

题目链接 动态开点的树链剖分qwq. 跟小奇的花园一模一样,不做过多讲解. #include<cstdio> #include<cstring> #include<cctype> #include<cstdlib> #include<algorithm> #define maxn 100010 #define mid ((l+r)>>1) #define check(x) if(x==0) x=++tot; using namespa

午学树链剖分有感~

今天上课谈到树链剖分,被机房某dalao嘲讽了一波,决定冒着不听课的风险学链剖 关于这篇blog的灵感来源:传送门,不妥删 1.0前置知识 1.1链式向前星 这个应该都会吧,但还是附上讲解,这个图很易懂啊,蒟蒻当时一直没学懂,看了图才发现自己有多弱智 1.2dfs序 这个学长上课讲的随便找了一个讲解,仅供参考:传送门 1.3线段树 线段树应该都学了,就不附连接了 1.4为什么要学链剖 小蒟蒻没怎么做过树上倍增和链剖的题,但据说树上倍增的题链剖都能做,但链剖的题树上倍增不一定能做 2.0正文 2.

BZOJ 2243: [SDOI2011]染色 树链剖分

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1886  Solved: 752[Submit][Status] Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写一个程序依次完成这m个操作. In