Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)
Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入文件的第一行为一个整数n,表示节点的个数。
接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。
接下来一行n个整数,第i个整数wi表示节点i的权值。
接下来1行,为一个整数q,表示操作的总数。
接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
Sample Output
4
1
2
2
10
6
5
6
5
16
Http
Luogu:https://www.luogu.org/problem/show?pid=2590
HYSBZ:http://www.lydsy.com/JudgeOnline/problem.php?id=1036
Source
树链剖分,最近公共祖先LCA,线段树
解决思路
这是一道树链剖分入门题。
考虑将树按照子树大小剖分成轻链和重链,然后用线段树来维护区间和和区间最大值。
需要注意的是,在树上进行链的跳转时,使用原编号,而若要用线段树查询或修改线段树时,要使用线段树中的编号(即第2次dfs中给点新分配的编号)
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int maxN=350000;
const int inf=2147483647;
class Edge//边
{
public:
int u,v,nex;
};
class SegmentTree//线段树
{
public:
ll mx,sum;//维护最大值和区间和
SegmentTree()
{
mx=0;
sum=0;
}
};
class Tree_Chain//树链剖分中需要维护的一些值
{
public:
int f,sz,top,hson,depth;//父节点,大小,向上跳的最高位置,重儿子,深度
};
int n;
//Tree 原树的一些数据
int cnt;
int Head[maxN];
Edge E[maxN*2];
ll W[maxN];//点权
Tree_Chain T[maxN];//树链剖分维护的值
int dfn[maxN];//记录树链剖分后的点的新编号(按照先重链再轻链)
SegmentTree Seg[maxN*4];//线段树
void Add_Edge(int u,int v);
//Tree_Chain
void dfs1(int u,int father);//第一遍dfs求出T中的一些数据
void dfs2(int u,int Top);//第二遍dfs构造Top和分配新编号
ll LCA_sum(int u,int v);//求解区间和
ll LCA_max(int u,int v);//求解区间最大值
//SegmentTree
void Update(int l,int r,int now,int pos,int key);//线段树单点更新
ll Query_sum(int l,int r,int now,int ql,int qr);//查询区间和
ll Query_max(int l,int r,int now,int ql,int qr);//查询区间最大值
int main()
{
cnt=0;
memset(Head,-1,sizeof(Head));
scanf("%d",&n);
for (int i=1;i<n;i++)//输入
{
int u,v;
scanf("%d%d",&u,&v);
Add_Edge(u,v);
Add_Edge(v,u);
}
T[1].f=0;//默认1为根节点
T[1].depth=1;
dfs1(1,1);//第一遍dfs
cnt=0;//标号置为0
dfs2(1,1);//第二遍dfs
for (int i=1;i<=n;i++)//输入点权
scanf("%lld",&W[i]);
for (int i=1;i<=n;i++)
Update(1,n,1,dfn[i],W[i]);//将点权按照新编号放入线段树
int qus;
scanf("%d",&qus);
while (qus--)//处理操作
{
char str[10];
scanf("%s",str);
if (str[0]=='Q')
{
int u,v;
scanf("%d%d",&u,&v);
if (str[1]=='M')
printf("%lld\n",LCA_max(u,v));//查询最大值
else
printf("%lld\n",LCA_sum(u,v));//查询和
}
else
{
int pos,key;
scanf("%d%d",&pos,&key);
pos=dfn[pos];
Update(1,n,1,pos,key);//单点修改
}
}
return 0;
}
void Add_Edge(int u,int v)//在图中加一条边
{
cnt++;
E[cnt].nex=Head[u];
Head[u]=cnt;
E[cnt].u=u;
E[cnt].v=v;
return;
}
void dfs1(int u,int father)//第一遍dfs
{
T[u].sz=1;//初始化基本信息
T[u].hson=0;
for (int i=Head[u];i!=-1;i=E[i].nex)
{
int v=E[i].v;
if (v==father)
continue;
T[v].f=u;//将子节点信息更新
T[v].depth=T[u].depth+1;
dfs1(v,u);
T[u].sz=T[u].sz+T[v].sz;
if ((T[u].hson==0)||(T[T[u].hson].sz<T[v].sz))//求出重儿子
T[u].hson=v;
}
return;
}
void dfs2(int u,int Top)//第二遍dfs,Top是当前传下来的能到达的重链顶端,当u==Top时,为轻链
{
cnt++;
dfn[u]=cnt;//分配新编号
T[u].top=Top;//分配Top
if (T[u].hson==0)//没有重儿子,说明到了叶子节点
return;
dfs2(T[u].hson,Top);//让重儿子继承重链
for (int i=Head[u];i!=-1;i=E[i].nex)
{
int v=E[i].v;
if ((v==T[u].f)||(v==T[u].hson))
continue;
dfs2(v,v);//其他儿子开轻链
}
return;
}
ll LCA_max(int u,int v)//求解u到v路径上的最大点权
{
ll mx=-inf;
while (T[u].top!=T[v].top)
{
if (T[T[u].top].depth<T[T[v].top].depth)//保证每一次u的深度尽量大
swap(u,v);
mx=max(mx,Query_max(1,n,1,dfn[T[u].top],dfn[u]));//让深度大的向上跳,注意这里要转成线段树中的编号
u=T[T[u].top].f;
}
if (dfn[v]<dfn[u])//保证u的深度更小
swap(u,v);
mx=max(mx,Query_max(1,n,1,dfn[u],dfn[v]));//查询链
return mx;
}
ll LCA_sum(int u,int v)//求解u到v上的点权之和,与上面基本一样
{
ll sum=0;
while (T[u].top!=T[v].top)
{
if (T[T[u].top].depth<T[T[v].top].depth)
swap(u,v);
sum=sum+Query_sum(1,n,1,dfn[T[u].top],dfn[u]);
u=T[T[u].top].f;
}
if (dfn[v]<dfn[u])
swap(u,v);
sum=sum+Query_sum(1,n,1,dfn[u],dfn[v]);
return sum;
}
void Update(int l,int r,int now,int pos,int key)//线段树单点修改
{
if (l==r)
{
Seg[now].mx=Seg[now].sum=key;
return;
}
int mid=(l+r)/2;
if (pos<=mid)
Update(l,mid,now*2,pos,key);
if (pos>=mid+1)
Update(mid+1,r,now*2+1,pos,key);
Seg[now].sum=Seg[now*2].sum+Seg[now*2+1].sum;
Seg[now].mx=max(Seg[now*2].mx,Seg[now*2+1].mx);
return;
}
ll Query_sum(int l,int r,int now,int ql,int qr)//线段树查询区间和
{
if ((l==ql)&&(r==qr))
return Seg[now].sum;
int mid=(l+r)/2;
if (qr<=mid)
return Query_sum(l,mid,now*2,ql,qr);
else if (ql>=mid+1)
return Query_sum(mid+1,r,now*2+1,ql,qr);
else
return Query_sum(l,mid,now*2,ql,mid)+Query_sum(mid+1,r,now*2+1,mid+1,qr);
}
ll Query_max(int l,int r,int now,int ql,int qr)//线段树查询区间最大值
{
if ((l==ql)&&(r==qr))
return Seg[now].mx;
int mid=(l+r)/2;
if (qr<=mid)
return Query_max(l,mid,now*2,ql,qr);
if (ql>=mid+1)
return Query_max(mid+1,r,now*2+1,ql,qr);
return max(Query_max(l,mid,now*2,ql,mid),Query_max(mid+1,r,now*2+1,mid+1,qr));
}
Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)的更多相关文章
- HDU5266 LCA 树链剖分LCA 线段树
HDU5266 LCA Description 给一棵 n 个点的树,Q 个询问 [L,R] : 求点 L , 点 L+1 , 点 L+2 -- 点 R 的 LCA. Input 多组数据. The ...
- bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 10677 Solved: 4313[Submit ...
- bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)
3626: [LNOI2014]LCA Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1272 Solved: 451[Submit][Status ...
- bzoj 2243 [SDOI2011]染色(树链剖分,线段树)
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4637 Solved: 1726[Submit][Status ...
- HDU 4366 Successor(树链剖分+zkw线段树+扫描线)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4366 [题目大意] 有一个公司,每个员工都有一个上司,所有的人呈树状关系,现在给出每个人的忠诚值和 ...
- 【洛谷5439】【XR-2】永恒(树链剖分,线段树)
[洛谷5439][XR-2]永恒(树链剖分,线段树) 题面 洛谷 题解 首先两个点的\(LCP\)就是\(Trie\)树上的\(LCA\)的深度. 考虑一对点的贡献,如果这两个点不具有祖先关系,那么这 ...
- bzoj 4034 [HAOI2015] T2(树链剖分,线段树)
4034: [HAOI2015]T2 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1536 Solved: 508[Submit][Status] ...
- poj 3237 Tree(树链剖分,线段树)
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 7268 Accepted: 1969 Description ...
- 【BZOJ3531】旅行(树链剖分,线段树)
[BZOJ3531]旅行(树链剖分,线段树) 题面 Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教 ...
随机推荐
- Ionic 3 延迟加载(Lazy Load)实战(一)
本文分享并演示了在 Ionic 3 框架中如何进行模块的延迟加载(Lazy Load)开发. 在我的实战课程「快速上手Ionic3 多平台开发企业级问答社区」中,因为开发的仿知乎 App 模块间的加载 ...
- Apache之Rewrite和RewriteRule规则梳理以及http强转https的配置总结
一. 简单实例介绍一般来说,apache配置好http和https后,如果想要做http强转到https,需要设置url重定向规则,大致需要下面几个步骤即可完成配置: 1)在httpd.conf文件里 ...
- ProxySQL实现Mysql读写分离 - 部署手册
ProxySQL是一个高性能的MySQL中间件,拥有强大的规则引擎.ProxySQL是用C++语言开发的,也是percona推的一款中间件,虽然也是一个轻量级产品,但性能很好(据测试,能处理千亿级的数 ...
- Docker容器学习梳理 - 基础环境安装
以下是centos系统安装docker的操作记录 1)第一种方法:采用系统自带的docker安装,但是这一般都不是最新版的docker安装epel源[root@docker-server ~]# wg ...
- Python初始编码-3
01010100 新11010000 开11010100 一01100000 家11000000 看11000000 看 01010100011101110101011110110A B C01000 ...
- 生产者消费者模式 php 【转】
在工作中常常听到某某大牛之间的交谈会涉及到,xx消费者啊啥的,到底什么大牛之间讲的是什么? 这篇文章主要解决三个问题: 1.到底什么是生产者和消费者,以及它们之间的故事 2.它们之间靠什么交流 3.应 ...
- spring cloud bus原理总结
1.spring cloud bus spring cloud是按照spring的配置对一系列微服务框架的集成,spring cloud bus是其中一个微服务框架,用于实现微服务之间的通信. spr ...
- 我的github地址
链接:https://github.com/long0123/test.git 推送项目的github的大致步骤如下: 1.在本地创建一个项目仓库,可以放些基本的项目文件 2.cd至该目录下 3. ...
- git工具
1.Git Bash常用命令: pwd 当前工作目录 clear 清屏 ls 列举当前目录下的文件及文件夹 cd 更改目录 mkdir 创建目录 touch 创建空文件 cp 拷 ...
- 数学战神app(小学生四则运算app)开发需求及进度
项目名字:“数学战神” 开发环境:Android eclipse 团队名称:战神联盟 团队成员:陈思明,许家豪,王宏财,吴旭涛 在之前的四则运算APP中添加更多的实用功能,并在各种平台推广宣传. 预加 ...