题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4127

题解

首先区间绝对值和可以转化为 \(2\) 倍的区间正数和 \(-\) 区间和。于是问题就转化为区间正数和。

因为每一次增加的量 \(d \geq 0\),所以每一个数只会被从负数变成正数一次。也就是说,从负变正的操作最多出现 \(n\) 次。

于是我们考虑在线段树上对于从负数变成正数的操作暴力修改。

如何判断一个区间内有负数变成正数的操作呢。令 \(c\) 表示这个区间的最大负数。于是如果 \(-k \leq c \leq 0\) 那么说明区间有数从负变正。

那么每一次从负变正都会走 \(\log n\) 个线段树节点,于是所有数从负变正而来的总时间为 \(O(n\log n)\)。

其余的修改的复杂度为 \(O(q\log n)\)。


时间复杂度为 \(O((n+q)\log n)\)。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;} typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii; template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
} #define lc o << 1
#define rc o << 1 | 1 const int N = 1e5 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f; int n, m, dfc;
int a[N];
int dep[N], f[N], siz[N], son[N], dfn[N], pre[N], top[N]; struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); } struct Node { ll sum, add, sumc, maxf; int s; } t[N << 2];
inline void pushup(int o, int L, int R) {
t[o].s = t[lc].s + t[rc].s;
t[o].sum = t[lc].sum + t[rc].sum + t[o].add * (R - L + 1);
t[o].sumc = t[lc].sumc + t[rc].sumc + t[o].add * t[o].s;
t[o].maxf = -INF;
if (t[lc].maxf) smax(t[o].maxf, t[lc].maxf);
if (t[rc].maxf) smax(t[o].maxf, t[rc].maxf);
if (t[o].maxf == -INF) t[o].maxf = 0;
t[o].maxf && (t[o].maxf += t[o].add);
// dbg("o = %d, L = %d, R = %d, t[o].maxf = %lld, t[o].s = %d\n", o, L, R, t[o].maxf, t[o].s);
assert(t[o].maxf || (t[o].maxf == 0 && t[o].s == R - L + 1));
}
inline void pushdown(int o, int L, int R) {
if (!t[o].add) return;
int M = (L + R) >> 1;
t[lc].sum += t[o].add * (M - L + 1), t[lc].add += t[o].add;
t[lc].sumc += t[o].add * t[lc].s, t[lc].maxf && (t[lc].maxf += t[o].add);
t[rc].sum += t[o].add * (R - M), t[rc].add += t[o].add;
t[rc].sumc += t[o].add * t[rc].s, t[rc].maxf && (t[rc].maxf += t[o].add);
t[o].add = 0;
}
inline void build(int o, int L, int R) {
t[o].add = 0;
if (L == R) {
t[o].sum = a[pre[L]];
t[o].maxf = std::min(0, a[pre[L]]);
t[o].sumc = std::max(a[pre[L]], 0);
t[o].s = a[pre[L]] >= 0;
return;
}
int M = (L + R) >> 1;
build(lc, L, M), build(rc, M + 1, R);
pushup(o, L, R);
}
inline void qadd(int o, int L, int R, int l, int r, int k) {
// dbg("qadd : o = %d, L = %d, R = %d, l = %d, r = %d, k = %d, t[o].maxf = %lld, t[o].sum = %lld, t[o].sumc = %lld, t[o].s = %d, t[o].add = %lld\n", o, L, R, l, r, k, t[o].maxf, t[o].sum, t[o].sumc, t[o].s, t[o].add);
if (l <= L && R <= r && (!t[o].maxf || t[o].maxf < -k)) {
t[o].sum += k * (ll)(R - L + 1);
t[o].sumc += k * (ll)t[o].s;
t[o].add += k;
if (t[o].maxf) t[o].maxf += k;
return;
}
if (L == R) {
t[o].sum += k, t[o].add += k;
t[o].sumc = std::max(0ll, t[o].sum);
t[o].s = t[o].sum >= 0;
t[o].maxf = std::min(0ll, t[o].sum);
return;
}
int M = (L + R) >> 1;
pushdown(o, L, R);
if (l <= M) qadd(lc, L, M, l, r, k);
if (r > M) qadd(rc, M + 1, R, l, r, k);
pushup(o, L, R);
}
inline ll qsum(int o, int L, int R, int l, int r) {
// dbg("qsum : o = %d, L = %d, R = %d, l = %d, r = %d\n", o, L, R, l, r);
if (l <= L && R <= r) return t[o].sumc * 2 - t[o].sum;
int M = (L + R) >> 1;
pushdown(o, L, R);
if (r <= M) return qsum(lc, L, M, l, r);
if (l > M) return qsum(rc, M + 1, R, l, r);
return qsum(lc, L, M, l, r) + qsum(rc, M + 1, R, l, r);
} inline void upd(int x, int y, int k) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
qadd(1, 1, n, dfn[top[x]], dfn[x], k);
x = f[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
qadd(1, 1, n, dfn[x], dfn[y], k);
}
inline ll qry(int x, int y) {
ll ans = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
ans += qsum(1, 1, n, dfn[top[x]], dfn[x]);
x = f[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
return ans += qsum(1, 1, n, dfn[x], dfn[y]);
} inline void dfs1(int x, int fa = 0) {
dep[x] = dep[fa] + 1, f[x] = fa, siz[x] = 1;
for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
// dbg("x = %d, pa = %d\n", x, pa);
top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
if (!son[x]) return; dfs2(son[x], pa);
for fec(i, x, y) if (y != son[x] && y != f[x]) dfs2(y, y);
} inline void work() {
dfs1(1), dfs2(1, 1), build(1, 1, n);
while (m--) {
int opt, x, y, z;
read(opt), read(x), read(y);
if (opt == 1) read(z), upd(x, y, z);
else printf("%lld\n", qry(x, y));
}
} inline void init() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
int x, y;
for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
} int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}

bzoj4127 Abs 树链剖分+线段树+均摊分析的更多相关文章

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

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

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

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

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

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

  4. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  5. BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树

    题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...

  6. BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树

    题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...

  7. 【bzoj3083】遥远的国度 树链剖分+线段树

    题目描述 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn ...

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

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

  9. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  10. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

随机推荐

  1. 如何降低Vue.js项目中Webpack打包文件的大小?

    https://blog.csdn.net/maray/article/details/50988500?utm_source=blogxgwz0 import Blur from ‘vux/src/ ...

  2. fedora18 You might need to install dependency packages for libxcb.

    22 down vote The page Qt for X11 Requirements lists some packages required to build Qt on Debian. Th ...

  3. Linux 用户必须知道的 14 个常用 Linux 终端快捷键

    简介:以下是一些每个 Linux 用户必须使用的键盘快捷键. 使用命令行时,这些 Linux 快捷键将提升你的工作效率和效率. 你知道什么把专业用户和普通用户分开的吗?掌握键盘快捷键. 好的!这虽不是 ...

  4. code_action

    w https://raw.githubusercontent.com/laravel/laravel/master/config/database.php <?php return [ /* ...

  5. postgresql_action

    SELECT * FROM x123_area a LEFT JOIN x123_user_task_brief utb ON utb.ref_area_code = a.area_code WHER ...

  6. day48—JavaScript键盘事件

    转行学开发,代码100天——2018-05-03 今天继续学习JavaScript事件基础之键盘事件. 键盘代号获取 keyCode 键盘事件:onkeydown onkeyup 如通过键盘上下左右按 ...

  7. 开机自启动Powershell脚本

    目录 目录 前言 修改注册表 写批处理 以管理员方式打开Posershell程序 修改PS-profile 最后 前言 这绝B是个非常受用的技能. 修改注册表 Open Registry Editor ...

  8. protel封装总结(新手必看)

    零件封装是指实际零件焊接到电路板时所指示的外观和焊点的位置.是纯粹的空间概念.因此不同的元件可共用同一零件封装,同种元件也可有不同的零件封装.像电阻,有传统的针插式,这种元件体积较大,电路板必须钻孔才 ...

  9. 002/Node.js(Mooc)--Http知识

    1.什么是Http 菜鸟教程:http://www.runoob.com/http/http-tutorial.html 视频地址:https://www.imooc.com/video/6713 h ...

  10. levelDB SSTable-1

    创建sstable文件 了解了sstable文件的存储格式,以及Data Block的组织,下面就可以分析如何创建sstable文件了.相关代码在table_builder.h/.cc以及block_ ...