题意

给定一棵树,设计数据结构支持以下操作

1 u v d  表示将路径 (u,v) 加d(d>=0)

2 u v	表示询问路径 (u,v) 上点权绝对值的和

分析

绝对值之和不好处理,那么我们开两棵线段树,一个存正数,一个存负数.然后对于两棵线段树,都要维护子树sz(有效节点数),sum(有效节点权值之和),lz(加法懒标记).特别的,因为负数可能会加到正数,那么修改一个区间的时候,询问一下这个区间最大的负数加上d有没有变成正数,如果有就暴力从负数线段树中删去这个节点,加入正数线段树中.又题目中满足每一次加的数都是非负的,也就是说一个数最多从负变成正一次,那么时间复杂度均摊是O(nlogn)O(nlogn)O(nlogn)的(每次删除/插入是lognlognlogn的).注意细节就行了…

当然考虑到一个位置要么在正树要么在负树,那么其实可以只写一棵线段树,如果当前区间最大负数+d仍然是负数就打上懒标记返回,否则暴力下传.线段树时间复杂度仍是O(nlogn)O(nlogn)O(nlogn).加上树链剖分就是O(nlog2n)O(nlog^2n)O(nlog2n)啦.

CODE

看着我7000B的代码陷入沉思…为毛打个线段树板题都要220行+

(其实两棵树大多数函数可以直接复制)

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
char cb[1<<15],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &res) {
char ch; int flg = 1; for(;!isdigit(ch=getc());)if(ch=='-')flg=-flg;
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0'); res*=flg;
}
const int MAXN = 100005;
const LL INF = 1e16;
int n, m, cnt, tmr, w[MAXN], a[MAXN], fir[MAXN], fa[MAXN], dfn[MAXN], top[MAXN], sz[MAXN], son[MAXN], dep[MAXN];
struct edge { int to, nxt; }e[MAXN<<1];
inline void Add(int u, int v) { e[cnt] = (edge){ v, fir[u] }, fir[u] = cnt++; }
void dfs(int x, int ff) { //树剖
dep[x] = dep[fa[x]=ff] + (sz[x]=1);
for(int v, i = fir[x]; ~i; i = e[i].nxt)
if((v=e[i].to) != ff) {
dfs(v, x), sz[x] += sz[v];
if(sz[v] > sz[son[x]]) son[x] = v;
}
}
void dfs2(int x, int tp) {
top[x] = tp; w[dfn[x] = ++tmr] = a[x];
if(son[x]) dfs2(son[x], tp);
for(int v, i = fir[x]; ~i; i = e[i].nxt)
if((v=e[i].to) != fa[x] && v != son[x])
dfs2(v, v);
} struct node { //写个结构体存最大值以及坐标
LL val; int id;
node(){}
node(LL v, int i):val(v), id(i){}
inline node operator +(const node &o)const {
if(val < o.val) return o;
else return *this;
}
}; namespace PositiveTree { //正线段树
LL sum[MAXN<<2], lz[MAXN<<2]; int sz[MAXN<<2];
inline void update(int i) {
sum[i] = sum[i<<1] + sum[i<<1|1];
sz[i] = sz[i<<1] + sz[i<<1|1];
}
inline void pushdown(int i) {
if(lz[i]) {
lz[i<<1] += lz[i]; sum[i<<1] += 1ll * sz[i<<1] * lz[i];
lz[i<<1|1] += lz[i]; sum[i<<1|1] += 1ll * sz[i<<1|1] * lz[i];
lz[i] = 0;
}
}
void build(int i, int l, int r) {
if(l == r) {
sum[i] = w[l] >= 0 ? w[l] : 0;
sz[i] = w[l] >= 0 ? 1 : 0;
return;
}
int mid = (l + r) >> 1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r);
update(i);
}
void insert(int i, int l, int r, int x, int y, int val) {
if(x == l && y == r) {
sum[i] += 1ll * sz[i] * val;
lz[i] += val;
return;
}
int mid = (l + r) >> 1;
pushdown(i);
if(y <= mid) insert(i<<1, l, mid, x, y, val);
else if(x > mid) insert(i<<1|1, mid+1, r, x, y, val);
else insert(i<<1, l, mid, x, mid, val), insert(i<<1|1, mid+1, r, mid+1, y, val);
update(i);
}
void modify(int i, int l, int r, int x, int val) {
if(l == r) {
sum[i] = val, sz[i] = 1; return;
}
int mid = (l + r) >> 1;
pushdown(i);
if(x <= mid) modify(i<<1, l, mid, x, val);
else modify(i<<1|1, mid+1, r, x, val);
update(i);
}
LL querysum(int i, int l, int r, int x, int y) {
if(x == l && y == r) return sum[i];
int mid = (l + r) >> 1;
pushdown(i);
LL res;
if(y <= mid) res = querysum(i<<1, l, mid, x, y);
else if(x > mid) res = querysum(i<<1|1, mid+1, r, x, y);
else res = querysum(i<<1, l, mid, x, mid) + querysum(i<<1|1, mid+1, r, mid+1, y);
update(i);
return res;
}
} namespace NegativeTree { //负线段树
node mx[MAXN<<2];
LL sum[MAXN<<2], lz[MAXN<<2]; int sz[MAXN<<2];
inline void update(int i) {
sum[i] = sum[i<<1] + sum[i<<1|1];
mx[i] = mx[i<<1] + mx[i<<1|1];
sz[i] = sz[i<<1] + sz[i<<1|1];
}
inline void pushdown(int i) {
if(lz[i]) {
lz[i<<1] += lz[i]; sum[i<<1] += 1ll * sz[i<<1] * lz[i]; mx[i<<1].val += lz[i];
lz[i<<1|1] += lz[i]; sum[i<<1|1] += 1ll * sz[i<<1|1] * lz[i]; mx[i<<1|1].val += lz[i];
lz[i] = 0;
}
}
void build(int i, int l, int r) {
if(l == r) {
mx[i] = node(w[l] < 0 ? w[l] : -INF, l);
sz[i] = w[l] < 0 ? 1 : 0;
sum[i] = w[l] < 0 ? w[l] : 0; return;
}
int mid = (l + r) >> 1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r);
update(i);
}
void insert(int i, int l, int r, int x, int y, int val) {
if(x == l && y == r) {
sum[i] += 1ll * sz[i] * val;
lz[i] += val;
mx[i].val += val;
return;
}
int mid = (l + r) >> 1;
pushdown(i);
if(y <= mid) insert(i<<1, l, mid, x, y, val);
else if(x > mid) insert(i<<1|1, mid+1, r, x, y, val);
else insert(i<<1, l, mid, x, mid, val), insert(i<<1|1, mid+1, r, mid+1, y, val);
update(i);
}
void modify(int i, int l, int r, int x) {
if(l == r) {
sum[i] = sz[i] = 0, mx[i].val = -INF; return;
}
int mid = (l + r) >> 1;
pushdown(i);
if(x <= mid) modify(i<<1, l, mid, x);
else modify(i<<1|1, mid+1, r, x);
update(i);
}
node querymx(int i, int l, int r, int x, int y) {
if(x == l && y == r) return mx[i];
int mid = (l + r) >> 1;
pushdown(i);
node res;
if(y <= mid) res = querymx(i<<1, l, mid, x, y);
else if(x > mid) res = querymx(i<<1|1, mid+1, r, x, y);
else res = querymx(i<<1, l, mid, x, mid) + querymx(i<<1|1, mid+1, r, mid+1, y);
update(i);
return res;
}
LL querysum(int i, int l, int r, int x, int y) {
if(x == l && y == r) return sum[i];
int mid = (l + r) >> 1;
pushdown(i);
LL res;
if(y <= mid) res = querysum(i<<1, l, mid, x, y);
else if(x > mid) res = querysum(i<<1|1, mid+1, r, x, y);
else res = querysum(i<<1, l, mid, x, mid) + querysum(i<<1|1, mid+1, r, mid+1, y);
update(i);
return res;
}
void ADD(int l, int r, int val) {
if(r < l) return;
node res = querymx(1, 1, n, l, r);
if(res.val + val >= 0) {
modify(1, 1, n, res.id); //暴力在负线段树中删
PositiveTree::modify(1, 1, n, res.id, res.val + val); //暴力在正线段树中插入
ADD(l, res.id-1, val);
ADD(res.id+1, r, val);
}
else insert(1, 1, n, l, r, val);
}
} inline void Plus(int u, int v, int d) {
while(top[u]!=top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
PositiveTree::insert(1, 1, n, dfn[top[u]], dfn[u], d);
NegativeTree::ADD(dfn[top[u]], dfn[u], d);
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u, v);
PositiveTree::insert(1, 1, n, dfn[v], dfn[u], d);
NegativeTree::ADD(dfn[v], dfn[u], d);
} inline LL Query(int u, int v) {
LL res = 0;
while(top[u]!=top[v]) {
if(dep[top[u]] < dep[top[v]]) swap(u, v);
res += - NegativeTree::querysum(1, 1, n, dfn[top[u]], dfn[u]) + PositiveTree::querysum(1, 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u, v);
return res - NegativeTree::querysum(1, 1, n, dfn[v], dfn[u]) + PositiveTree::querysum(1, 1, n, dfn[v], dfn[u]);
} int main() {
memset(fir, -1, sizeof fir);
read(n), read(m);
for(int i = 1; i <= n; ++i) read(a[i]);
for(int x, y, i = 1; i < n; ++i)
read(x), read(y), Add(x, y), Add(y, x);
dfs(1, 0), dfs2(1, 1);
NegativeTree::build(1, 1, n);
PositiveTree::build(1, 1, n);
int op, u, v, d;
while(m--) {
read(op), read(u), read(v);
if(op == 1) read(d), Plus(u, v, d);
else printf("%lld\n", Query(u, v));
}
}

BZOJ 4127: Abs (树链剖分 线段树求区间绝对值之和 带区间加法)的更多相关文章

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

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

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

    BZOJ.1036 [ZJOI2008]树的统计Count (树链剖分 线段树维护和与最值) 题意分析 (题目图片来自于 这里) 第一道树链剖分的题目,谈一下自己的理解. 树链剖分能解决的问题是,题目 ...

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

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

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

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

  5. 【bzoj4127】Abs 树链剖分+线段树

    题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...

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

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

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

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

  8. BZOJ4127Abs——树链剖分+线段树

    题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...

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

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

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

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

随机推荐

  1. Oracle-DQL 3- 单行函数

    单行函数: --使用函数对表中的数据进行运算和处理,针对每行数据返回一个结果,叫做单行函数--包括数字函数,字符函数,日期函数,转换函数,其他函数 1.数字函数 --round(m,n) 将数字m精确 ...

  2. oracle导出空表

    1.先查询数据库空表 select 'alter table '||table_name||' allocate extent;' from user_tables where num_rows=0 ...

  3. Thinkphp+Ajax带关键词搜索列表无刷新分页实例

    Thinkphp+Ajax带关键词搜索列表无刷新分页实例,两个查询条件,分页和搜索关键字,懂的朋友还可以添加其他分页参数. 搜索#keyword和加载内容区域#ajax_lists <input ...

  4. DRF 序列化组件 序列化的两种方式 反序列化 反序列化的校验

    序列化组件 django自带的有序列化组件不过不可控不建议使用(了解) from django.core import serializers class Books(APIView): def ge ...

  5. table表格整体居中 和 table表格中各行各列内容居中

    1.table表格整个居中<div style="text-align: center;"> <table border="1" style= ...

  6. Redis主从及哨兵

    Redis主从用法 像MySQL一样,redis是支持主从同步的,而且也支持一主多从以及多级从结构. 主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的SORT就可以由从服务器来承 ...

  7. bat 将war文件转换成ear文件

    1.无需拷贝war文件,自动获取war set path=%path%;D:\jdk\jdk1.6.0_31\bin;C:\Program Files\7-Zip del **0001-control ...

  8. linux 阿里云 新机器 安装jdk1.8

    2019年7月17日15:58:34 按着顺序来: wget https://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4f ...

  9. Ubuntu 忘记系统登录密码,如何修改密码

    Ubuntu 忘记系统登录密码,如何修改密码. 1.重新启动,按ESC键进入Boot Menu,选择recovery mode(一般是第二个选项). 2.在#号提示符下用cat /etc/shadow ...

  10. Spark 源码和应用开发环境的构建

    引言 Spark 现在无疑是大数据领域最热门的技术之一,读者很容易搜索到介绍如何应用 Spark 技术的文章,但是作为开发人员,在了解了应用的概念之后,更习惯的是打开开发环境,开发一些应用来更深入的学 ...