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要在访问完子树之后计算,见代码:
void dfs1(int x)
{
num[x]++;
for(int i=;i<map[x].size();i++)
{
int dd=map[x][i];
if(dd!=fa[x])
{
fa[dd]=x;
deep[dd]=deep[x]+;
dfs1(dd);
num[x]+=num[dd];
}
}
}
注释:map是STL的vector,用来储存边。
第二个dfs完成son[x],tree[x],ftree[x]的计算,代码如下:
void dfs2(int x)
{
topa++;
ftree[topa]=x;
A[topa]++;
tree[x]=topa;
int zi=,mx=;
for(int i=;i<map[x].size();i++)
{
int dd=map[x][i];
if(num[dd]>mx)
{
mx=num[dd];
zi=dd;
}
}
if(zi!=) dfs2(zi); else return;
son[x]=zi;
for(int i=;i<map[x].size();i++)
{
int dd=map[x][i];
if(dd!=zi) dfs2(dd);
}
}
剖分动作结束,接下来是线段树的事情了。
这里再说一下如何在线段树上操作原树,之前提到过,其实在线段树上也有原来树的结构。
x的子树区间就是tree[x]到tree[x]+num[x]-1。
下面来看一下这道题:codevs 1228 苹果树
这是一个最基本的树链剖分。题目中要求计算一颗子树上有苹果多少颗,改变是点修改。因此只要找到那个节点,子树在线段树上的位置,线段树是维护某区间的苹果树数量,查询操作就是一般的线段树查询。
代码:
#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int maxn=; vector<int> map[maxn];
int fa[maxn],n,deep[maxn],num[maxn],topa,A[maxn],tree[maxn],ftree[maxn],son[maxn],sumv[maxn*],k; void dfs1(int x)
{
num[x]++;
for(int i=;i<map[x].size();i++)
{
int dd=map[x][i];
if(dd!=fa[x])
{
fa[dd]=x;
deep[dd]=deep[x]+;
dfs1(dd);
num[x]+=num[dd];
}
}
} void dfs2(int x)
{
topa++;
ftree[topa]=x;
A[topa]++;
tree[x]=topa;
int zi=,mx=;
for(int i=;i<map[x].size();i++)
{
int dd=map[x][i];
if(num[dd]>mx)
{
mx=num[dd];
zi=dd;
}
}
if(zi!=) dfs2(zi); else return;
son[x]=zi;
for(int i=;i<map[x].size();i++)
{
int dd=map[x][i];
if(dd!=zi) dfs2(dd);
}
} void init(int o,int L,int R)
{
if(L==R) sumv[o]=A[L];
else
{
int M=(L+R)/;
init(o*,L,M);
init(o*+,M+,R);
sumv[o]=sumv[o*]+sumv[o*+];
}
} int y1,y2,p;
void update(int o,int L,int R)
{
if(L==R) sumv[o]=(sumv[o]+)%;
else
{
int M=(L+R)/;
if(p<=M) update(o*,L,M);
else update(o*+,M+,R);
sumv[o]=sumv[o*]+sumv[o*+];
}
} int ans;
void query(int o,int L,int R)
{
if(y1<=L && R<=y2) ans+=sumv[o];
else
{
int M=(L+R)/;
if(y1<=M) query(o*,L,M);
if(y2>M) query(o*+,M+,R);
}
} int main()
{
cin>>n;
for(int i=,x,y;i<=n-;i++)
{
cin>>x>>y;
map[x].push_back(y);
}
deep[]=;
dfs1();
dfs2(); init(,,n); cin>>k;
for(int i=,x;i<=k;i++)
{
char tp;
cin>>tp;
if(tp=='C')
{
cin>>x;
p=tree[x];
update(,,n);
}
else
{
cin>>x;
y1=tree[x];
y2=y1+num[x]-;
ans=;
query(,,n);
cout<<ans<<endl;
}
}
return ;
}
codevs 1228 苹果树 树链剖分讲解的更多相关文章
- Codevs 4633 [Mz]树链剖分练习
4633 [Mz]树链剖分练习 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 大师 Master 题目描述 Description 给定一棵结点数为n的树,初始点权均为0,有依次q个 ...
- 【最近公共祖先】【树链剖分】CODEVS 1036 商务旅行
树链剖分求lca模板.O(log(n)),就是不倍增嘛~ #include<cstdio> #include<algorithm> using namespace std; # ...
- cogs 2450. 距离 树链剖分求LCA最近公共祖先 快速求树上两点距离 详细讲解 带注释!
2450. 距离 ★★ 输入文件:distance.in 输出文件:distance.out 简单对比时间限制:1 s 内存限制:256 MB [题目描述] 在一个村子里有N个房子,一 ...
- 【BZOJ-3589】动态树 树链剖分 + 线段树 + 线段覆盖(特殊的技巧)
3589: 动态树 Time Limit: 30 Sec Memory Limit: 1024 MBSubmit: 405 Solved: 137[Submit][Status][Discuss] ...
- BZOJ 2157: 旅游( 树链剖分 )
树链剖分.. 样例太大了根本没法调...顺便把数据生成器放上来 -------------------------------------------------------------------- ...
- USACO 2015 December Contest, Platinum Problem Max Flow【树链剖分】
题意比较难理解,就是给你n个点的树,然后给你m个修改操作,每一次修改包括一个点对(x, y),意味着将x到y所有的点权值加一,最后问你整个树上的点权最大是多少. 比较裸的树链剖分了,感谢Haild的讲 ...
- CODEVS.1228 苹果树(DFS序)
To CODEVS.1228 苹果树 To poj 3321 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结出很多的苹果.卡卡非常喜欢吃苹果,所以他一直都精心的呵护这 ...
- 【Luogu】P3313旅行(树链剖分)
题目链接 动态开点的树链剖分qwq. 跟小奇的花园一模一样,不做过多讲解. #include<cstdio> #include<cstring> #include<cct ...
- 树链剖分学习笔记 By cellur925
先%一发机房各路祖传树剖大师%%%. 近来总有人向我安利树剖求LCA,然鹅我还是最爱树上倍增.然鹅又发现近年一些题目(如天天爱跑步.运输计划等在树上进行操作的题目),我有把树转化为一条链求解的思路,但 ...
随机推荐
- AHCI: Failed to attach drive to Port1 (VERR_GENERAL_FAILURE).
在mac操作系统下,安装VirtualBoxVm虚拟机,虚拟机里面安装wind7操作系统.在启动虚拟机的时候报错:AHCI: Failed to attach drive to Port1 (VERR ...
- linux2.6 内存管理——逻辑地址转换为线性地址(逻辑地址、线性地址、物理地址、虚拟地址)
Linux系统中的物理存储空间和虚拟存储空间的地址范围分别都是从0x00000000到0xFFFFFFFF,共4GB,但物理存储空间与虚拟存储空间布局完全不同.Linux运行在虚拟存储空间,并负责把系 ...
- C# 设置Excel打印选项及打印excel文档
C# 设置Excel打印选项及打印excel文档 打印Excel文档是一个很常见的操作,但有时候我们会碰到各种不同的打印需求,例如只打印一个Excel工作表的其中一部分,或打印时每页都有表头,或把工作 ...
- YYModel 源码解读(二)之YYClassInfo.h (1)
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END 为了兼容Swift 中的 ? 和 ! oc 在6.3引入了两个新的类型注释:__nullable和__non ...
- 1元搭建自己的云服务器&解析域名
最近在学做微信开发,没有自己的域名和服务器就不得不寄人篱下,索性自己就到云主机上搭建了个服务器,但是水平有限弄了一个下午~~有自己的域名和服务器的好处相信不用我多说了.比如日后可以有自己域名的个性博客 ...
- 数百个 HTML5 例子学习 HT 图形组件 – WebGL 3D 篇
<数百个 HTML5 例子学习 HT 图形组件 – 拓扑图篇>一文让读者了解了 HT的 2D 拓扑图组件使用,本文将对 HT 的 3D 功能做个综合性的介绍,以便初学者可快速上手使用 HT ...
- react初始(1)
前言:react框架的出现是因为Facebook在建设Instagram交流平台的时候要处理大量的数据流,但是行业流行的MVC框架并不能适合Facebook公司的要求,他们就组织了自己的人力来开发re ...
- PHP 装饰器模式
装饰器模式:是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. [装饰器模式中主要角色] 抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这 ...
- 应用SuperIO(SIO)和开源跨平台物联网框架ServerSuperIO(SSIO)构建系统的整体方案
SSIO的更新 在SSIO上增加了UDP通讯方式,可以到Github上下载源代码.在原来的项目中,远端的设备与中心站的数据交互并没有使用过UDP方式.这种短连接的通讯链路,不容易维护,主要体现在:(1 ...
- Java 内存区域与内存溢出
内存区域 Java 虚拟机在执行 Java 程序的过程中会把他所管理的内存划分为若干个不同的数据区域.Java 虚拟机规范将 JVM 所管理的内存分为以下几个运行时数据区:程序计数器.Java 虚拟机 ...