spoj - Grass Planting(树链剖分模板题)
Grass Planting
题意
给出一棵树,树有边权。每次给出节点 (u, v) ,有两种操作:1. 把 u 到 v 路径上所有边的权值加 1。2. 查询 u 到 v 的权值之和。
分析
如果这些值不是在树上,而是在区间上,那么凭借线段树、树状数组可以很轻松的解决,但是在树上则不能直接操作。
树链剖分就是将树上的节点映射到区间上,从而实现区间操作。
学习树链剖分前需要掌握的知识点:线段树、LCA。
认真读完这篇 blog ,跟着算法流程走一遍差不多就懂了。
code
#include<bits/stdc++.h>
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 10;
int n, m;
int fa[MAXN]; // fa[v]: v 的父亲
int dep[MAXN]; // dep[v]: v 的深度(根深度为1)
int siz[MAXN]; // : 以 v 为根的子树的节点数
int son[MAXN]; // : 重儿子,siz[u] 为 v 的子节点中 siz 值最大的,那么 u 就是 v 的重儿子
int top[MAXN]; // : 表示 v 所在的重链的顶端节点
int w[MAXN]; // : 表示 v 与其父亲节点的连边在线段树中的位置
int num; // 将树映射到线段树上的标号
int cnt, head[MAXN];
struct Edge {
int to, next;
}edge[MAXN];
void addedge(int u, int v) {
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void dfs(int u) {
siz[u] = 1; son[u] = 0;
for(int i = head[u]; ~i; i = edge[i].next) {
if(edge[i].to != fa[u]) {
fa[edge[i].to] = u;
dep[edge[i].to] = dep[u] + 1;
dfs(edge[i].to);
if(siz[edge[i].to] > siz[son[u]]) son[u] = edge[i].to;
siz[u] += siz[edge[i].to];
}
}
}
void build_tree(int u, int tp) {
w[u] = ++num; top[u] = tp;
if(son[u]) build_tree(son[u], top[u]); // 使重链各边在线段树中呈连续分布
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
if(v != son[u] && v != fa[u])
build_tree(v, v);
}
}
ll sum[MAXN], add[MAXN];
void pushUp(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushDown(int rt, int m) {
if(add[rt]) {
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
sum[rt << 1] += (m - (m >> 1)) * add[rt];
sum[rt << 1 | 1] += (m >> 1) * add[rt];
add[rt] = 0;
}
}
void build(int l, int r, int rt) {
add[rt] = sum[rt] = 0;
if(l == r) return;
int m = (l + r) / 2;
build(lson); build(rson);
}
void update(int L, int R, int l, int r, int rt) {
if(L <= l && R >= r) {
sum[rt] += r - l + 1;
add[rt] += 1;
return;
}
pushDown(rt, r - l + 1);
int m = (l + r) / 2;
if(m >= L) update(L, R, lson);
if(m < R) update(L, R, rson);
pushUp(rt);
}
ll query(int L, int R, int l, int r, int rt) {
if(L <= l && R >= r) return sum[rt];
pushDown(rt, r - l + 1);
int m = (l + r) / 2;
ll res = 0;
if(m >= L) res += query(L, R, lson);
if(m < R) res += query(L, R, rson);
return res;
}
void change(int v, int u) {
int t1 = top[v], t2 = top[u];
while(t1 != t2) {
if(dep[t1] < dep[t2]) {
swap(t1, t2); swap(v, u);
}
update(w[t1], w[v], 1, n, 1);
v = fa[t1]; t1 = top[v];
}
if(v == u) return;
if(dep[v] > dep[u]) swap(v, u);
update(w[son[v]], w[u], 1, n, 1);
}
ll seek(int v, int u) {
int t1 = top[v], t2 = top[u];
ll res = 0;
while(t1 != t2) {
if(dep[t1] < dep[t2]) {
swap(t1, t2); swap(v, u);
}
res += query(w[t1], w[v], 1, n, 1);
v = fa[t1]; t1 = top[v];
}
if(v == u) return res;
if(dep[v] > dep[u]) swap(v, u);
return res + query(w[son[v]], w[u], 1, n, 1);
}
int main() {
memset(head, -1, sizeof head);
cnt = num = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs(1);
build_tree(1, 1);
build(1, n, 1);
while(m--) {
char cc[2];
int u, v;
scanf("%s%d%d", cc, &u, &v);
if(cc[0] == 'P') change(u, v);
else printf("%lld\n", seek(u, v));
}
return 0;
}
spoj - Grass Planting(树链剖分模板题)的更多相关文章
- BZOJ 2243 染色 | 树链剖分模板题进阶版
BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...
- SPOJ QTREE Query on a Tree【树链剖分模板题】
树链剖分,线段树维护~ #include <cstdio> #include <cstring> #include <iostream> #include < ...
- bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题
[ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u ...
- 洛谷 P3384 树链剖分(模板题)
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
- BZOJ 1036 树的统计Count 树链剖分模板题
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1036 题目大意: 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将 ...
- 洛谷 P2146 [NOI2015]软件包管理器 (树链剖分模板题)
题目描述 Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个 ...
- P3833 [SHOI2012]魔法树 (树链剖分模板题)
题目链接:https://www.luogu.org/problem/P3833 题目大意:有一颗含有n个节点的树,初始时每个节点的值为0,有以下两种操作: 1.Add u v d表示将点u和v之间的 ...
- 【BZOJ 1036】【ZJOI 2008】树的统计 树链剖分模板题
sth神犇的模板: //bzoj1036 题目:一个n个点的树每个点有一个权值,支持修改单点权值,求某两点路径上的点权和或最大点权. #include <cstdio> using nam ...
- BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 14982 Solved: 6081[Submit ...
随机推荐
- DOS程序员手册(二)
2.6存储设备 随着DOS的升级,磁盘存储容量也有了很大扩充.表2.4介绍了软盘容量的增加以 及所支持驱动器型号的数量. ...
- jmeter察看结果树响应数据中文乱码解决办法
1.到jmeter目录文件中bin文件夹下找到jmeter.properties文件,该文件为jmeter配置文件.使用编辑工具打开它. 2.找到 #sampleresult.default.e ...
- ssm项目中ueditor富文本编辑器的使用
一.下载 https://ueditor.baidu.com/website/index.html 将ueditor放到项目中合适的位置 二 . 配置文件上传路径 在utf8-jsp/jsp/conf ...
- Linux(Ubuntu 命令大全)
Ubuntu 一. Ubuntu简介 Ubuntu(乌班图)是一个基于Debian的以桌面应用为主的Linux操作系统,据说其名称来自非洲南部祖鲁语或科萨语的“ubuntu”一词,意思是“人性”.“我 ...
- JavaWeb笔记(五)JSP
JSP 指令 格式: <%@ 指令名称 属性名1=属性值1 属性名2=属性值2 ... %> 分类: page 配置JSP页面 contentType:等同于response.setCon ...
- [转] Linux命令行编辑常用键
ctrl + a 将光标移动到命令行开头相当于VIM里shift+^ ctrl + e 将光标移动到命令行结尾处相当于VIM里shift+$ ctrl + 方向键左键 光标移动到前一个单词开头 ctr ...
- update-database -script
update-database -script 更新脚本生成失败? 项目选择的不对 update后面-database空格-script
- kvm竟然抓不到kvm的tracepoint
今天终于把kvm给搭起来了,打开了host机的tracepoint竟然一个都没有抓到,这是咋回事? 难道kvm的东西只有在启动的时候才会被抓到? 虚拟出来一块内存一块CPU,虚拟出来一个内存.感觉都好 ...
- P2507 [SCOI2008]配对
题目背景 四川NOI2008省选 题目描述 你有 n 个整数Ai和n 个整数Bi.你需要把它们配对,即每个Ai恰好对应一个Bp[i].要求所有配对的整数差的绝对值之和尽量小,但不允许两个相同的数配对. ...
- 深入解析vue.js响应式原理与实现
vue.js响应式原理解析与实现.angularjs是通过脏检查来实现数据监测以及页面更新渲染.之后,再接触了vue.js,当时也一度很好奇vue.js是如何监测数据更新并且重新渲染页面.vue.js ...