bzoj4127 Abs 树链剖分+线段树+均摊分析
题目传送门
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 树链剖分+线段树+均摊分析的更多相关文章
- 【bzoj4127】Abs 树链剖分+线段树
题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...
- BZOJ4127Abs——树链剖分+线段树
题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- HDU4897 (树链剖分+线段树)
Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树
题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...
- 【bzoj3083】遥远的国度 树链剖分+线段树
题目描述 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- BZOJ2243 (树链剖分+线段树)
Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...
- POJ3237 (树链剖分+线段树)
Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...
随机推荐
- Matlab 中 函数circshift()的用法
a = [ ; ; ]; b = [- - -; - - -;- - -]; c = [ ; ; ]; Hist(:,:,) = a; Hist(:,:,) = b; Hist(:,:,) = c; ...
- Linux随笔 - vi/vim 编辑器显示行号
显示行号 1. 打开vi 编辑器 2. 输入 :set number 3. 回车 关闭行号显示 1. 打开vi 编辑器 2. 输入 :set nonumber 3. 回车 行号在每次打开 vi/v ...
- Vue知识整理5:v-bind绑定属性(Class 与 Style 绑定)
通过v-bind实现Class 与 Style 绑定,方便调整属性的值
- curl发json
linux 模拟post请求 curl -X POST \ -H "Content-Type: application/json" \ -H "token:GXJP1cl ...
- Week 10 - 474. Ones and Zeroes
474. Ones and Zeroes In the computer world, use restricted resource you have to generate maximum ben ...
- oracle 11g 数据库恢复技术 ---02 控制文件
oracle 11g 数据库恢复技术 ---02 控制文件 SYS@ orcl >show parameter control_file NAME TYPE VALUE ------------ ...
- 黑群晖DSM 6.x 配置文件grub.cfg修改 mac地址/sn等修改
新的DSM 6.x配置文件和以前的XPEnoboot的配置文件不一样了,我们可以通过OSFMount虚拟光驱软件打开img后再修改. 安装完成后运行OSFMount点击左下角-Mount new,选择 ...
- 使用内核LED框架搭建驱动 ——led_classdev_register
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...
- NameVirtualHost的使用
如果某个ip:port 上只设置了一个虚拟主机,那么NameVirtualHost ip:port 可以不写,因为从这个ip:port的访问不需要做任何选择:如上例中192.168.1.197只对应于 ...
- 6、 逻辑回归(Logistic Regression)
6.1 分类问题 在分类问题中,你要预测的变量