BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值)

题意分析

(题目图片来自于 这里)

第一道树链剖分的题目,谈一下自己的理解。

树链剖分能解决的问题是,题目中反复要求对链上信息进行修改和查询。如果依旧采取用dfs序的方法,会发现不适用,原因是dfs序适用于处理子树的问题。当然暴力肯定是要被卡掉的。

树链剖分分为两次dfs。

第一次dfs求出每个节点的father,son,size,deep,这里涉及到重儿子的概念,网上有很多资料就不在这里赘述了。换句话说,通过第一次dfs,我们求出了树上的重儿子(size最大的那个儿子)。

第二次dfs,就将重儿子相互连接形成重链,换句话说,求出每个节点的top(链顶),树上的每个节点,可以根据top是否相同来判断是否在一条链上,还有在线段树上的newid和线段树newid对应的实际的节点编号hashback。,因此在处理树上链修改的时候,可以很方便得进行处理。

有了这些数据,就可以对树上每个节点建立线段树。其实newid,可以看做是树上节点到线段树叶子节点的一个hash,而数组hashback,相当于线段树叶子节点到树上节点的hash。

通过建立的线段树,不难发现,在一链上的节点,所对应线段树区间是连续的。

在维护线段树的时候,和普通的线段树没有区别。只是查询时,需要trick。

这里阐述2个,查询链上最值和链上节点权值和。

先说第一个。我们根据题目知道了两个节点编号x,y.通过两次dfs的顺序,不难得出,deep大的节点对应的newid就大(想一想为什么,可以画一画)。于是就从xy中选择一个deep比较大的,让他向上爬,每次爬选择到他的top(链顶)。为什么爬到链顶呢?原因很简单,刚才说过一句话,在一链上的节点,所对应线段树区间是连续的。,换句话说,对于这段连续的区间,就可以利用线段树的特性方便求出他的最值。 如此就能将复杂度讲的很低。但是问题又来了,如果他不在一条链上,自己很孤立,没有形成重链或者轻链,那么他就向自己的父亲爬就好了。如此一来,我们每次选择深度大的点,向上爬,一边爬一遍更新信息,知道两个人都爬到lca(x,y)时,结束。

对于第二个,差别仅仅在于每次爬的时候用不同的方式更新信息罢了。刚才是求得最值,现在只求和,区别也仅仅在此,思路是一样的。

至于单点修改,和线段树的修改是没有区别的,只不过修改的节点要用newid保存的值hash过去,否则会出错。

代码总览

#include <bits/stdc++.h>
#define ll int
#define nmax 30820
using namespace std;
int fa[nmax],son[nmax],sz[nmax],newid[nmax],hashback[nmax],dep[nmax],top[nmax];
int num,tot,head[nmax],data[nmax];
struct edge{
int to;
int next;
}edg[nmax<<1];
struct tree{
int l,r,val,mx;
int mid(){
return (l+r)>>1;
}
}tree[nmax<<2];
void add(int u, int v){
edg[tot].to = v;
edg[tot].next = head[u];
head[u] = tot++;
}
void dfsFirst(int rt, int f,int d){
dep[rt] = d;
fa[rt] =f;
sz[rt] = 1;
for(int i = head[rt]; i!= -1; i = edg[i].next){
int nxt = edg[i].to;
if(nxt != f){
dfsFirst(nxt,rt,d+1);
sz[rt]+=sz[nxt];
if(son[rt] == -1 || sz[nxt] > sz[son[rt]]){
son[rt] = nxt;
}
} }
}
void dfsSecond(int rt, int tp){
top[rt] = tp;
newid[rt] = ++num;
hashback[num] = rt;
if(son[rt] == -1) return;
dfsSecond(son[rt],tp);
for(int i = head[rt];i != -1; i = edg[i].next){
int nxt = edg[i].to;
if(nxt != son[rt] && nxt != fa[rt])
dfsSecond(nxt,nxt);
}
}
void init(){
memset(tree,0,sizeof tree);
memset(head,-1,sizeof head);
memset(son,-1,sizeof son);
memset(edg,0,sizeof edg);
memset(hashback,0,sizeof hashback);
tot = num = 0;
}
void PushUp(int rt){
tree[rt].mx = max(tree[rt<<1].mx , tree[rt<<1|1].mx);
tree[rt].val = tree[rt<<1].val + tree[rt<<1|1].val;
}
void Build(int l, int r, int rt){
tree[rt].l = l; tree[rt].r = r;
if(l == r){
tree[rt].val = tree[rt].mx = data[hashback[l]];
return;
}
Build(l,tree[rt].mid(),rt<<1);
Build(tree[rt].mid()+1,r,rt<<1|1);
PushUp(rt);
}
void UpdatePoint(int val, int pos, int rt){
if(tree[rt].l == tree[rt].r){
tree[rt].mx = tree[rt].val = val ;
return;
}
if(pos <= tree[rt].mid()) UpdatePoint(val,pos,rt<<1);
else UpdatePoint(val,pos,rt<<1|1);
PushUp(rt);
}
int QueryMAX(int l,int r,int rt){
if(l <= tree[rt].l && tree[rt].r <= r) return tree[rt].mx;
//PushDown(rt);
int ans = -1e9+7;
if(l <= tree[rt].mid()) ans = max(ans,QueryMAX(l,r,rt<<1));
if(r > tree[rt].mid()) ans = max(ans,QueryMAX(l,r,rt<<1|1));
return ans;
}
int QuerySUM(int l,int r,int rt)
{
if(l>tree[rt].r || r<tree[rt].l) return 0;
if(l <= tree[rt].l && tree[rt].r <= r) return tree[rt].val;
return QuerySUM(l,r,rt<<1) + QuerySUM(l,r,rt<<1|1);
}
int Find_MAX(int x, int y){
int tx = top[x],ty =top[y],ans = -1e9+7;
while(tx != ty){
if(dep[tx] < dep[ty]){
swap(x,y);
swap(tx,ty);
}
ans = max(ans,QueryMAX(newid[tx],newid[x],1));
x = fa[tx]; tx = top[x];
}
if(dep[x] > dep[y]) ans = max(ans,QueryMAX(newid[y],newid[x],1));
else ans = max(ans,QueryMAX(newid[x],newid[y],1));
return ans;
}
int Find_SUM(int x, int y){
int tx = top[x],ty =top[y],ans = 0;
while(tx != ty){
if(dep[tx] < dep[ty]){
swap(x,y);
swap(tx,ty);
}
ans += QuerySUM(newid[tx],newid[x],1);
x = fa[tx]; tx = top[x];
}
if(dep[x] > dep[y]) ans += QuerySUM(newid[y],newid[x],1);
else ans += QuerySUM(newid[x],newid[y],1);
return ans;
} int n,m;
int main()
{
//freopen("in.txt","r",stdin);
init();
scanf("%d",&n);
int u,v,m,x,y;
char op[10];
for(int i =1;i<=n-1;++i){
scanf("%d %d",&u,&v);
add(u,v);
add(v,u);
}
for(int i =1;i<=n;++i) scanf("%d", &data[i]);
dfsFirst(1,0,1);
dfsSecond(1,1);
// printf("MESSA ID DATA FA SON SIZE DEEP NEWID TOP\n");
// for(int i = 1;i<=n;++i){
// printf("DEBUG %5d %5d %5d %5d %5d %5d %5d %5d\n",i,data[i],fa[i],son[i],sz[i],dep[i],newid[i],top[i]);
// }
Build(1,n,1);
// printf("MESSA ID val\n");
// for(int i = 1;i<=n;++i){
// printf("DEBUF %d %d\n",i,QuerySUM(i,i,1));
// }
scanf("%d",&m);
for(int i = 0;i<m;++i){
scanf("%s %d %d",op,&x,&y);
if(op[1] == 'S'){//QSUM
printf("%d\n",Find_SUM(x,y));
}else if(op[1] == 'M'){//QMAX
printf("%d\n",Find_MAX(x,y));
}else{
UpdatePoint(y,newid[x],1);
data[x] = y;
}
}
return 0;
}

BZOJ.1036 [ZJOI2008]树的统计Count ( 点权树链剖分 线段树维护和与最值)的更多相关文章

  1. 【bzoj1036】树的统计[ZJOI2008]树链剖分+线段树

    题目传送门:1036: [ZJOI2008]树的统计Count 这道题是我第一次打树剖的板子,虽然代码有点长,但是“打起来很爽”,而且整道题只花了不到1.5h+,还是一遍过样例!一次提交AC!(难道前 ...

  2. BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)

    题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...

  3. BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 )

    BZOJ.4034 [HAOI2015]树上操作 ( 点权树链剖分 线段树 ) 题意分析 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 ...

  4. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  5. bzoj 4196 [Noi2015]软件包管理器 (树链剖分+线段树)

    4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2852  Solved: 1668[Submit][Sta ...

  6. bzoj 2157: 旅游【树链剖分+线段树】

    裸的树链剖分+线段树 但是要注意一个地方--我WA了好几次才发现取完相反数之后max值和min值是要交换的-- #include<iostream> #include<cstdio& ...

  7. BZOJ 3589 动态树 (树链剖分+线段树)

    前言 众所周知,90%90\%90%的题目与解法毫无关系. 题意 有一棵有根树,两种操作.一种是子树内每一个点的权值加上一个同一个数,另一种是查询多条路径的并的点权之和. 分析 很容易看出是树链剖分+ ...

  8. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  9. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

随机推荐

  1. 基于神念TGAM的脑波小车(4)

    我使用的是HC05和BT06俩个蓝牙模块 1.[AT模式]HC05蓝牙模块的PIO11接VCC,上电后即进入HC05AT指令模式,对于BT06蓝牙直接上电进入AT模式,用USBT06转TTL模块连接到 ...

  2. linux一切皆文件之文件描述符(一)

    一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件.如:普通文件.目录.字符设备.块设备.套接字等 2.当一个文件被进程打开,就会创建一个文件描述符.这时候,文件的路径就 ...

  3. Cocos2d-x的跨平台原理

    为了充分发挥硬件性能,手机游戏通常使用Native App开发模式,这就造成开发商要为iOS 和Android平台用户开发不同的应用,无论是产品迭代还是运行维护都非常麻烦.Cocos2d-x在iOS, ...

  4. 基于Linux-3.9.4内核增加简单的时间片轮转功能

    简单的时间片轮转多道程序内核代码 原创作品转载请注明出处https://github.com/mengning/linuxkernel/ 作者:sa18225465 一.安装 Linux-3.9.4 ...

  5. 微信JS-SDK实现上传图片功能

    最近在项目开发中,有一个在微信WEB项目中上传图片的需求,一开始使用了传统的<input type="file">的方式去实现,但是后面发现在使用这种传统模式时会由于手 ...

  6. Python20 - Day09

    python并发编程之多线程理论 1.什么是线程? 进程只是用来把资源集中到一起(进程是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位. 多线程(多个控制线程)的概念是,在一个进程中存在 ...

  7. caffe 预训练 或者Fine-Tuning 操作

    1.使用预训练模型,需要修改训练的prototxt,将layer name改为与要使用模型的layer name相同即可. Borrowing Weights from a Pretrained Ne ...

  8. Daily Srum 10.30

    Android那一组打算用SQL Server这个关系型数据库,而王鹿鸣他们一组却是依赖于Hbase,这是一件很麻烦的事,所以我打算在这两方面都建立一个数据库.虽然挺麻烦,但是还是为了扩展性所做的必要 ...

  9. web06-PanduanLogin

    电影网站:www.aikan66.com 项目网站:www.aikan66.com 游戏网站:www.aikan66.com 图片网站:www.aikan66.com 书籍网站:www.aikan66 ...

  10. Leetcode题库——34.在排序数组中国查找元素的第一个和最后一个位置

    @author: ZZQ @software: PyCharm @file: searchRange.py @time: 2018/11/12 19:19 要求:给定一个按照升序排列的整数数组 num ...