bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题
[ZJOI2008]树的统计Count
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. 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”的形式给出。
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
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
solution:
这非常明显,是一个树链剖分的模板题。
树链剖分就是把一棵树分成若干条链,分为轻链和重链,并把点映射到一个线性数列,然后用线段树维护这个序列。
对于点u,size最大的儿子称为重儿子,其他的儿子叫轻儿子。e(u,v)为重边,u到u的轻儿子的边称为轻边。重链是由重边组成的链。
性质1:u的任意轻儿子v,有size[v]<=size[u]/2
证明:假设size[v]>size[u]/2,设u的重儿子为v’.
size[v’]>=size[v]
size[v’]+size[v]>=2size[v]>size[u]矛盾
性质2:从根到任意点的路径中,轻边与重链的数量都小于等于lg n
证明:
1、假设路径只有轻边,每经过一条轻边,size至少减一半,所以最多lg n条轻边
2、每经过一条重边,size不会变大,所以1在有重边的时候仍然成立
3、连续的重边会连成重链,所以重链数<=轻边数+1
对于每一棵子树,优先搜索重儿子,每一个点的dfs序即为该点在线段树上的位置。
显然,一棵子树中的点是一个连续的区间,一条重链上的点也是一个连续的区间。
先一次dfs1算出每一个点的深度deep,重儿子son,子树大小size,父亲fa
再一次dfs2算出每一个点的w和top,w表示该点在线段树上的标号,top表示该点所在的重链的顶点。
void dfs1(int u){
siz[u]=;
son[u]=;
for(int i=head[u];i;i=Next[i]){
int v=vet[i];
if(v!=fa[u]){
fa[v]=u;
deep[v]=deep[u]+;
dfs1(v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v])
son[u]=v;
}
}
}
void dfs2(int u,int tp){
w[u]=++num;
top[u]=tp;
if(son[u])
dfs2(son[u],tp);
for(int i=head[u];i;i=Next[i])
if(vet[i]!=fa[u]&&vet[i]!=son[u])
dfs2(vet[i],vet[i]);
}
然后构建线段树
void insert(int u,int l,int r,int i,int val){
if(l==r){
rmax[u]=sum[u]=val;
return;
}
int mid=(l+r)>>;
if(i<=mid)
insert(u+u,l,mid,i,val);
else
insert(u+u+,mid+,r,i,val);
sum[u]=sum[u+u]+sum[u+u+];
rmax[u]=max(rmax[u+u],rmax[u+u+]);
}
for(int i=;i<=n+n+n+n+n;i++)
rmax[i]=-;
for(int i=;i<=n;i++){
int x;
scanf("%d",&x);
insert(,,n,w[i],x);
}
然后还有查询和修改
单点修改非常简单,直接用线段树的insert
void insert(int u,int l,int r,int i,int val){
if(l==r){
rmax[u]=sum[u]=val;
return;
}
int mid=(l+r)>>;
if(i<=mid)
insert(u+u,l,mid,i,val);
else
insert(u+u+,mid+,r,i,val);
sum[u]=sum[u+u]+sum[u+u+];
rmax[u]=max(rmax[u+u],rmax[u+u+]);
}
scanf("%s",ca);
int x,y;
scanf("%d%d",&x,&y);
if(ca[]=='C'){
insert(,,n,w[x],y);
}
查询要分情况考虑
1、top[x]==top[y]
在同一条重链上
直接查询(x,y)区间
2、top[x]!=top[y]
向上跳一步,直到情况1
很不好解释,还是上代码
int query1(int u,int l,int r,int i,int j){
if(l==i&&r==j)
return rmax[u];
int mid=(l+r)>>;
if(j<=mid)
return query1(u+u,l,mid,i,j);
else
if(i>mid)
return query1(u+u+,mid+,r,i,j);
else
return max(query1(u+u,l,mid,i,mid),query1(u+u+,mid+,r,mid+,j));
}
int query2(int u,int l,int r,int i,int j){
if(l==i&&r==j)
return sum[u];
int mid=(l+r)>>;
if(j<=mid)
return query2(u+u,l,mid,i,j);
else
if(i>mid)
return query2(u+u+,mid+,r,i,j);
else
return query2(u+u,l,mid,i,mid)+query2(u+u+,mid+,r,mid+,j);
}
while(m--){
scanf("%s",ca);
int x,y;
scanf("%d%d",&x,&y);
if(ca[]=='C'){
insert(,,n,w[x],y);
}
else
if(ca[]=='Q'&&ca[]=='M'){
ans=-;
while(top[x]!=top[y]){
int f1=(top[x]==x?fa[x]:top[x]),f2=(top[y]==y?fa[y]:top[y]);
if(deep[f1]>=deep[f2]){
if(top[x]==x)
ans=max(ans,query1(,,n,w[x],w[x]));
else
ans=max(ans,query1(,,n,w[f1]+,w[x]));
x=f1;
}
else{
if(top[y]==y)
ans=max(ans,query1(,,n,w[y],w[y]));
else
ans=max(ans,query1(,,n,w[f2]+,w[y]));
y=f2;
}
}
ans=max(ans,query1(,,n,min(w[x],w[y]),max(w[x],w[y])));
printf("%d\n",ans);
}
else{
ans=;
while(top[x]!=top[y]){
int f1=(top[x]==x?fa[x]:top[x]),f2=(top[y]==y?fa[y]:top[y]);
if(deep[f1]>=deep[f2]){
if(top[x]==x)
ans+=query2(,,n,w[x],w[x]);
else
ans+=query2(,,n,w[f1]+,w[x]);
x=f1;
}
else{
if(top[y]==y)
ans+=query2(,,n,w[y],w[y]);
else
ans+=query2(,,n,w[f2]+,w[y]);
y=f2;
}
}
ans+=query2(,,n,min(w[x],w[y]),max(w[x],w[y]));
printf("%d\n",ans);
}
}
完整的ac代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
int ans,m,n,vet[],head[],Next[],en,fa[],deep[],siz[],son[],top[],w[],num,sum[],rmax[];
char ca[];
void addedge(int u,int v){
vet[++en]=v;
Next[en]=head[u];
head[u]=en;
}
void dfs1(int u){
siz[u]=;
son[u]=;
for(int i=head[u];i;i=Next[i]){
int v=vet[i];
if(v!=fa[u]){
fa[v]=u;
deep[v]=deep[u]+;
dfs1(v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v])
son[u]=v;
}
}
}
void dfs2(int u,int tp){
w[u]=++num;
top[u]=tp;
if(son[u])
dfs2(son[u],tp);
for(int i=head[u];i;i=Next[i])
if(vet[i]!=fa[u]&&vet[i]!=son[u])
dfs2(vet[i],vet[i]);
}
void insert(int u,int l,int r,int i,int val){
if(l==r){
rmax[u]=sum[u]=val;
return;
}
int mid=(l+r)>>;
if(i<=mid)
insert(u+u,l,mid,i,val);
else
insert(u+u+,mid+,r,i,val);
sum[u]=sum[u+u]+sum[u+u+];
rmax[u]=max(rmax[u+u],rmax[u+u+]);
}
int query1(int u,int l,int r,int i,int j){
if(l==i&&r==j)
return rmax[u];
int mid=(l+r)>>;
if(j<=mid)
return query1(u+u,l,mid,i,j);
else
if(i>mid)
return query1(u+u+,mid+,r,i,j);
else
return max(query1(u+u,l,mid,i,mid),query1(u+u+,mid+,r,mid+,j));
}
int query2(int u,int l,int r,int i,int j){
if(l==i&&r==j)
return sum[u];
int mid=(l+r)>>;
if(j<=mid)
return query2(u+u,l,mid,i,j);
else
if(i>mid)
return query2(u+u+,mid+,r,i,j);
else
return query2(u+u,l,mid,i,mid)+query2(u+u+,mid+,r,mid+,j);
}
int main(){
scanf("%d",&n);
for(int i=;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
deep[]=;
dfs1();
dfs2(,);
for(int i=;i<=n+n+n+n+n;i++)
rmax[i]=-;
for(int i=;i<=n;i++){
int x;
scanf("%d",&x);
insert(,,n,w[i],x);
}
scanf("%d",&m);
while(m--){
scanf("%s",ca);
int x,y;
scanf("%d%d",&x,&y);
if(ca[]=='C'){
insert(,,n,w[x],y);
}
else
if(ca[]=='Q'&&ca[]=='M'){
ans=-;
while(top[x]!=top[y]){
int f1=(top[x]==x?fa[x]:top[x]),f2=(top[y]==y?fa[y]:top[y]);
if(deep[f1]>=deep[f2]){
if(top[x]==x)
ans=max(ans,query1(,,n,w[x],w[x]));
else
ans=max(ans,query1(,,n,w[f1]+,w[x]));
x=f1;
}
else{
if(top[y]==y)
ans=max(ans,query1(,,n,w[y],w[y]));
else
ans=max(ans,query1(,,n,w[f2]+,w[y]));
y=f2;
}
}
ans=max(ans,query1(,,n,min(w[x],w[y]),max(w[x],w[y])));
printf("%d\n",ans);
}
else{
ans=;
while(top[x]!=top[y]){
int f1=(top[x]==x?fa[x]:top[x]),f2=(top[y]==y?fa[y]:top[y]);
if(deep[f1]>=deep[f2]){
if(top[x]==x)
ans+=query2(,,n,w[x],w[x]);
else
ans+=query2(,,n,w[f1]+,w[x]);
x=f1;
}
else{
if(top[y]==y)
ans+=query2(,,n,w[y],w[y]);
else
ans+=query2(,,n,w[f2]+,w[y]);
y=f2;
}
}
ans+=query2(,,n,min(w[x],w[y]),max(w[x],w[y]));
printf("%d\n",ans);
}
}
return ;
}
bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题的更多相关文章
- 【bzoj1036】[ZJOI2008]树的统计Count 树链剖分+线段树
题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v ...
- Cogs 1688. [ZJOI2008]树的统计Count(树链剖分+线段树||LCT)
[ZJOI2008]树的统计Count ★★★ 输入文件:bzoj_1036.in 输出文件:bzoj_1036.out 简单对比 时间限制:5 s 内存限制:162 MB [题目描述] 一棵树上有n ...
- BZOJ 2243 染色 | 树链剖分模板题进阶版
BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...
- 【BZOJ1036】[ZJOI2008]树的统计Count 树链剖分
[BZOJ1036][ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. ...
- BZOJ1036 [ZJOI2008]树的统计Count 树链剖分
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1036 题意概括 一个树,每个节点有一个权值.3种操作. 1:修改某一个节点的权值. 2:询问某两个 ...
- BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 14302 Solved: 5779[Submit ...
- Bzoj 1036: [ZJOI2008]树的统计Count 树链剖分,LCT
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 11102 Solved: 4490[Submit ...
- BZOJ 1036: [ZJOI2008]树的统计Count( 树链剖分 )
树链剖分... 不知道为什么跑这么慢 = = 调了一节课啊跪.. ------------------------------------------------------------------- ...
- bzoj 1036: [ZJOI2008]树的统计Count 树链剖分+线段树
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 16294 Solved: 6645[Submit ...
随机推荐
- poj3045 Cow Acrobats(二分最大化最小值)
https://vjudge.net/problem/POJ-3045 读题后提取到一点:例如对最底层的牛来说,它的崩溃风险=所有牛的重量-(底层牛的w+s),则w+s越大,越在底层. 注意范围lb= ...
- 7. Buffer_包描述文件_npm常用指令_fs文件读写_模块化require的规则
1. Buffer 一个和数组类似的对象,不同是 Buffer 是专门用来保存二进制数据的. 特点: 大小固定: 在创建时就确定了,且无法调整 性能较好: 直接对计算机的内存进行操作 每个元素大小为1 ...
- 16进制转化8进制---map
#include "stdio.h" #include "string.h" #include "string" #include &quo ...
- Java 基础:认识&理解关键字 native 实战篇
Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 泥瓦匠初次遇见 navicat 是在 java.lang.Object 源码中的一个hashCode方 ...
- SQLSERVER 聚集一个表的字段2008及以后,要求支持XML
将以下代码中的TABLE_NAME替换成所需表名称即可. 注意 declare 和set 语句后面不要有 :否则可能执行不成功 declare @S_Column varchar(8000)set @ ...
- 前言|Elena
2019.3.19更新置顶 2018.11.5更新置顶 2018.9.7更新置顶 -这里写下置顶- 嗨嗨嗨 这里AlenaNuna,偏远小渔村oi蒟蒻一只,各大题库id有Elena/AlenaNuna ...
- 大规模微服务架构下的Service Mesh探索之路
小结: 1. 第一.二代Service Mesh meetup-slides/敖小剑-蚂蚁金服-大规模微服务架构下的Service Mesh探索之路.pdf https://github.com/se ...
- oracle_dataGuard_11G
[李红]--切记_从库只安装实例_不需要 dbca 创建数据库 但是 netca 创建监听看个人喜欢,我反正是创建了.[DataGuard_主数据库的参数配置]1.启用 force logging 功 ...
- ASA failover
Active-Standby 1.作用:提供设备冗余 2.物理概念:primary 和 secondary ,需要命令敲得,角色不会切换, 3.虚拟概念:active和standby ,需要选举,角色 ...
- JVM内存问题分析
JVM运行时数据区: 1.方法区:类信息(类名,访问修饰符.字段描述.方法 描述等).常量.静态变量.即时编译后的class文件等.在GC时用永久代来实现方法区 2.运行时常量池:是方法区的一部分,存 ...