bzoj1036 树的统计 树链剖分模板
时间:2019-10-02
本文章向大家介绍bzoj1036 树的统计 树链剖分模板,主要包括bzoj1036 树的统计 树链剖分模板使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
题意:给出树上任意两点,求路径上的值的和与最大值,带单点修改操作
树链剖分思路:
1、对树进行dfs求出点的深度和父亲节点,然后求出轻重儿子(重儿子就是点最多的那个子树,其余都是轻儿子),用一个son数组指向每个节点的重儿子
2、对树进行第二次dfs,对于所有的重儿子,求出他的top节点也就是每个重儿子沿着重链可以到达的最远的那个祖先,然后维护dfs序,记录每个节点的访问次序以及第几次访问的是哪个节点,轻儿子的top节点就是本身
然后我们得到
dfs序: 1 4 9 13 14 8 10 3 7 2 6 11 12 5
top数组: 1 1 1 1 1 8 10 3 3 2 2 2 12 5(对应dfs序)
这样我们就把这棵树拆成了一条条的链(top值相同则为一条链上的点),用线段树维护这个dfs序,就可以快速求出链上最大值和值的和了,
对于任意两点,我们只需依次求出路径上的所有链的答案然后合并即可,可以证明路径上轻重链的条数是不超过logn的,这样单次查询的复杂度为O((logn)^2)
总时间复杂度O(q(logn)^2)
在计算两点答案的时候,采取一个巧妙的方法。首先若两个点不在同一条链上,我们总是让深度更大的那个点x往上跳到top[x],并统计这条链的答案,直到两个点到同一条链上,最后计算在一条链上时的答案即可
AC代码(模板)
#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; struct Edge { int v,next; }edge[N<<1]; int sum[N<<2],mx[N<<2],n; int head[N],tot,dep[N],fa[N],sz[N],son[N],top[N],id[N],rk[N],cnt,val[N]; void init() { memset(head,-1, sizeof(head)); tot=0; } void add(int u,int v) { edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } void dfs1(int u,int f) { dep[u]=dep[f]+1; fa[u]=f; sz[u]=1; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].v; if(v==f)continue; dfs1(v,u); sz[u]+=sz[v]; if(sz[v]>sz[son[u]])son[u]=v; } } void dfs2(int u,int t) { top[u]=t; id[u]=++cnt; rk[cnt]=u; if(!son[u])return; dfs2(son[u],t); for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].v; if(v!=son[u]&&v!=fa[u])dfs2(v,v); } } void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); } void build(int l,int r,int rt) { if(l==r) { mx[rt]=sum[rt]=val[rk[l]]; return; } int m=(l+r)>>1; build(l,m,rt<<1); build(m+1,r,rt<<1|1); pushup(rt); } int querySum(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R)return sum[rt]; int m=(l+r)>>1; int res=0; if(L<=m)res+=querySum(L,R,l,m,rt<<1); if(R>m)res+=querySum(L,R,m+1,r,rt<<1|1); return res; } int queryMax(int L,int R,int l,int r,int rt) { if(L<=l&&r<=R)return mx[rt]; int m=(l+r)>>1; int res=-1e9; if(L<=m)res=max(res,queryMax(L,R,l,m,rt<<1)); if(R>m)res=max(res,queryMax(L,R,m+1,r,rt<<1|1)); return res; } void update(int pos,int val,int l,int r,int rt) { if(l==r){ sum[rt]=mx[rt]=val; return; } int m=(l+r)>>1; if(pos<=m)update(pos,val,l,m,rt<<1); else update(pos,val,m+1,r,rt<<1|1); pushup(rt); } int getSum(int x,int y) { int ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); ans+=querySum(id[top[x]],id[x],1,n,1); x=fa[top[x]]; } if(id[x]>id[y])swap(x,y); ans+=querySum(id[x],id[y],1,n,1); return ans; } int getMax(int x,int y) { int ans=-1e9; while (top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]])swap(x,y); ans=max(ans,queryMax(id[top[x]],id[x],1,n,1)); x=fa[top[x]]; } if(id[x]>id[y])swap(x,y); ans=max(ans,queryMax(id[x],id[y],1,n,1)); return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); init(); int u,v; cin>>n; for(int i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } for(int i=1;i<=n;i++)cin>>val[i]; dfs1(1,1); dfs2(1,1); build(1,n,1); int q,x,y; string op; cin>>q; while(q--) { cin>>op>>x>>y; if(op=="QMAX")cout<<getMax(x,y)<<'\n'; else if(op=="QSUM")cout<<getSum(x,y)<<'\n'; else update(id[x],y,1,n,1); } return 0; }
原文地址:https://www.cnblogs.com/xusirui/p/11617658.html
- 全球最大家谱网站Ancestry.com意外泄露了30万名用户的登录凭证
- 摸金Redis漏洞
- 机器人越来越像人,你会担心你的工作被人工智能取代吗?
- 一句代码实现批量数据绑定[上篇]
- 机器学习-从高频号码中预测出快递送餐与广告骚扰
- MS Windows 下基于Atom的LaTeX编译环境的配置
- WCF中的Binding模型之一: Binding模型简介
- WCF中的Binding模型之一: Binding模型简介
- 2017最火的五篇深度学习论文 总有一篇适合你
- SplashScreenSource的妙用
- SplashScreenSource的妙用
- SplashScreenSource的妙用
- Nodejs学习笔记(十七)--- 浮点运算decimal.js
- AI时代让自己幸福更是一种能力
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- VirtualBox 错误:This kernel requires the following features not present on the CPU
- keepalived实现nginx高可用
- LINUX中详解AWK内建变量FS,NF,NR,RT,RS,ORS,OFS
- Centos7安装配置NFS服务和挂载教程(推荐)
- 轻松搞定VIM高亮NGINX配置文件的方法
- CentOS7 下安装telnet服务的实现方法
- Linux内核启动参数详解
- CentOS6.3添加nginx系统服务的实例详解
- Linux中在防火墙中开启80端口方法示例
- Apache 获取真实ip的配置的实现方法
- Linux下文件的切分与合并的简单方法介绍
- ubuntu 系统上为php加上redis 扩展的实现方法
- deepin linux 手动升级内核的方法
- UGL之单色位图
- Linux网络启动问题:Device does not seem to be present解决办法